<commit_before>//Author:        Uli Frankenfeld
//Last Modified: 06.12.2000

#include <math.h>
#include <string.h>
#include <iostream.h>
#include "AliL3Logging.h"
#include "AliL3TrackArray.h"
#include "AliL3HoughTrack.h"
#include "AliL3ConfMapTrack.h"
#include "AliL3TrackSegmentData.h"
#include "AliL3Transform.h"
#include "AliL3ConfMapPoint.h"
//_____________________________________________________________
//
// The L3 TrackArray
//

ClassImp(AliL3TrackArray)

AliL3TrackArray::AliL3TrackArray(){
  //Default constructor
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  fTrackType='t';
  SetSize();
}

AliL3TrackArray::AliL3TrackArray(Int_t ntrack){
  //Constructor.
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  fTrackType='t';
  SetSize(ntrack);
}

AliL3TrackArray::AliL3TrackArray(char* tracktype,Int_t ntrack){
  //Constructor.
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  if(strcmp(tracktype,"AliL3Track")==0) fTrackType='t';
  if(strcmp(tracktype,"AliL3ConfMapTrack")==0) fTrackType='c';
  if(strcmp(tracktype,"AliL3HoughTrack")==0) fTrackType='h';
  SetSize(ntrack);
}

AliL3TrackArray::AliL3TrackArray(char* tracktype){
  //Constructor.
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  if(strcmp(tracktype,"AliL3Track")==0) fTrackType='t';
  if(strcmp(tracktype,"AliL3ConfMapTrack")==0) fTrackType='c';
  if(strcmp(tracktype,"AliL3HoughTrack")==0) fTrackType='h';
  SetSize();
}

AliL3TrackArray::~AliL3TrackArray(){
  //Destructor
  DeleteArray();
}

AliL3Track *AliL3TrackArray::NextTrack(){
  if(fNTracks<fSize) return fTrack[fNTracks++];
  SetSize(fSize+100);
   return fTrack[fNTracks++];
}

void AliL3TrackArray::DeleteArray(){
  for(Int_t i=0; i<fSize;i++)
    delete fTrack[i];
  delete[] fIsPresent;
  delete[] fTrack;
}

Bool_t AliL3TrackArray::SetSize(Int_t newsize){
  if(newsize<=fSize) return kFALSE; //shrink comes later!!
  if(!fSize){
    fSize = newsize;
    fTrack = new AliL3Track*[fSize];
    fIsPresent = new Bool_t[fSize];
    switch(fTrackType){
      case 't':
        for(Int_t i=0;i<fSize;i++){
          fTrack[i]   = new AliL3Track();
          fIsPresent[i] = kTRUE;
        }
        break;
      case 'c':
        for(Int_t i=0;i<fSize;i++){
          fTrack[i]   = new AliL3ConfMapTrack();
          fIsPresent[i] = kTRUE;
        }
        break;
      case 'h':
        for(Int_t i=0;i<fSize;i++){
          fTrack[i]   = new AliL3HoughTrack();
          fIsPresent[i] = kTRUE;
        }
        break;
      default:
        return kFALSE;
    }
    return kTRUE;
  }
  AliL3Track **tmp = new AliL3Track*[fSize];
  Bool_t *pre = new Bool_t[fSize];
  for(Int_t i=0; i<fSize;i++){
    tmp[i] = fTrack[i];
    pre[i] = fIsPresent[i];
  }
  delete[]  fTrack;
  delete[] fIsPresent;
  fTrack =  new AliL3Track*[fSize];
  fIsPresent = new Bool_t[newsize];
  for(Int_t i=0; i<fSize;i++){
    fTrack[i]   = tmp[i];
    fIsPresent[i] = pre[i];
  }
  delete[] tmp;
  delete[] pre;
  switch(fTrackType){
    case 't':
      for(Int_t i=fSize;i<newsize;i++){
        fTrack[i]   = new AliL3Track();
        fIsPresent[i] = kTRUE;
      }
      break;
    case 'c':
      for(Int_t i=fSize;i<newsize;i++){
        fTrack[i]   = new AliL3ConfMapTrack();
        fIsPresent[i] = kTRUE;
      }
      break;
    case 'h':
      for(Int_t i=fSize;i<newsize;i++){
        fTrack[i]   = new AliL3HoughTrack();
        fIsPresent[i] = kTRUE;
      }
      break;
    default:
      return kFALSE;
  }
  fSize = newsize;
  return kTRUE;
}

void AliL3TrackArray::Reset(){
  fNTracks=0;
  fNAbsent=0;
  for(Int_t i=0; i<fSize;i++)
    fIsPresent[i] = kTRUE;
}

void AliL3TrackArray::Remove(Int_t track){
  if(fIsPresent[track]){
    fIsPresent[track]=kFALSE;
    fNAbsent++;
  }
}

void AliL3TrackArray::FillTracks(Int_t ntracks, AliL3TrackSegmentData* tr){
  //Read tracks from shared memory (or memory)
  AliL3TrackSegmentData *trs = tr;
  for(Int_t i=0; i<ntracks; i++){
    AliL3Track *track = NextTrack();
    track->SetPt(trs->fPt);
    track->SetPsi(trs->fPsi);
    track->SetTgl(trs->fTgl);
    track->SetNHits(trs->fNPoints);
    track->SetCharge(trs->fCharge);
    track->SetFirstPoint(trs->fX,trs->fY,trs->fZ);
    track->SetLastPoint(trs->fLastX,trs->fLastY,trs->fLastZ);
    track->SetHits( trs->fNPoints, trs->fPointIDs );
    UChar_t *tmpP = (UChar_t*)trs;
    tmpP += sizeof(AliL3TrackSegmentData)+trs->fNPoints*sizeof(UInt_t);
    trs = (AliL3TrackSegmentData*)tmpP;
  }
}

void AliL3TrackArray::FillTracks(Int_t ntracks, AliL3TrackSegmentData* tr,Int_t slice, AliL3Transform* trans){
  //Read tracks from shared memory (or memory)
  AliL3TrackSegmentData *trs = tr;
  for(Int_t i=0; i<ntracks; i++){
    AliL3Track *track = NextTrack();
    track->SetPt(trs->fPt);
    Float_t psi[1];
    psi[0]=trs->fPsi;
    trans->Local2GlobalAngle(psi,slice);
    track->SetPsi(psi[0]);
    track->SetTgl(trs->fTgl);
    track->SetNHits(trs->fNPoints);
    track->SetCharge(trs->fCharge);
    Float_t first[3];
    first[0]=trs->fX;first[1]=trs->fY;first[2]=trs->fZ;
    trans->Local2Global(first,slice);
    track->SetFirstPoint(first[0],first[1],first[2]);
    Float_t last[3];
    last[0]=trs->fLastX;last[1]=trs->fLastY;last[2]=trs->fLastZ;
    trans->Local2Global(last,slice);
    track->SetLastPoint(last[0],last[1],last[2]);
    track->SetHits( trs->fNPoints, trs->fPointIDs );
    UChar_t *tmpP = (UChar_t*)trs;
    tmpP += sizeof(AliL3TrackSegmentData)+trs->fNPoints*sizeof(UInt_t);
    trs = (AliL3TrackSegmentData*)tmpP;
  }
}

UInt_t AliL3TrackArray::GetOutSize(){
  UInt_t count = GetOutCount();   //use only present tracks
  UInt_t tHits = 0;
  for(Int_t i=0;i<fNTracks;i++){  //loop over all tracks
    AliL3Track *track = GetCheckedTrack(i);  //use only present tracks
    if(track)                                       //use only present tracks
      tHits += track->GetNHits();
  }

  //calculate size of track
  return count*sizeof(AliL3TrackSegmentData)+sizeof(UInt_t)*tHits;
}

UInt_t AliL3TrackArray::WriteTracks(UInt_t & ntracks,AliL3TrackSegmentData* tr){
  ntracks = GetOutCount();
  return WriteTracks(tr);
}

UInt_t AliL3TrackArray::WriteTracks(AliL3TrackSegmentData* tr){
  if(GetTrackType()=='c') return WriteConfMapTracks(tr);
  AliL3TrackSegmentData *tP = tr;
  UInt_t *pP;
  UInt_t size = 0;
  for(Int_t i=0; i<fNTracks; i++){  //loop over all tracks
    AliL3Track *track = GetCheckedTrack(i); //use only present tracks
    if(!track) continue;                           //use only present tracks
    tP->fX = track->GetFirstPointX();
    tP->fY = track->GetFirstPointY();
    tP->fZ = track->GetFirstPointZ();
    tP->fPt = track->GetPt();
    tP->fLastX = track->GetLastPointX();
    tP->fLastY = track->GetLastPointY();
    tP->fLastZ = track->GetLastPointZ();
    tP->fPsi = track->GetPsi();
    tP->fTgl = track->GetTgl();
    tP->fCharge = track->GetCharge();
    tP->fNPoints = track->GetNHits();
    pP = (UInt_t*)track->GetHitNumbers();
    for (UInt_t j=0;j<tP->fNPoints;j++){
      tP->fPointIDs[j] = pP[j];
    }
    Byte_t *tmpP = (Byte_t *)tP;
    tmpP += sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    size += sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    tP = (AliL3TrackSegmentData*)tmpP;
  }
  return size;
}

UInt_t AliL3TrackArray::WriteConfMapTracks(AliL3TrackSegmentData* tr){
  // use first and last point objects
  AliL3TrackSegmentData *tP = tr;
  UInt_t *pP;
  UInt_t size = 0;
  for(Int_t i=0; i<fNTracks; i++){  //loop over all tracks
    AliL3ConfMapTrack *track =(AliL3ConfMapTrack *) GetCheckedTrack(i); //use only present tracks
    if(!track) continue;                           //use only present tracks
    AliL3ConfMapPoint *hit = (AliL3ConfMapPoint*)track->lastHit;
    AliL3ConfMapPoint *lastHit = (AliL3ConfMapPoint*)track->firstHit;
    tP->fX = hit->GetX();
    tP->fY = hit->GetY();
    tP->fZ = hit->GetZ();
    tP->fLastX = lastHit->GetX();
    tP->fLastY = lastHit->GetY();
    tP->fLastZ = lastHit->GetZ();

//    tP->fX = track->GetFirstPointX();
//    tP->fY = track->GetFirstPointY();
//    tP->fZ = track->GetFirstPointZ();
    tP->fPt = track->GetPt();
//    tP->fLastX = track->GetLastPointX();
//    tP->fLastY = track->GetLastPointY();
//    tP->fLastZ = track->GetLastPointZ();
    tP->fPsi = track->GetPsi();
    tP->fTgl = track->GetTgl();
    tP->fCharge = track->GetCharge();
    tP->fNPoints = track->GetNHits();
    pP = (UInt_t*)track->GetHitNumbers();
    for (UInt_t j=0;j<tP->fNPoints;j++){
      tP->fPointIDs[j] = pP[j];
    }
    Byte_t *tmpP = (Byte_t *)tP;
    tmpP += sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    size +=sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    tP = (AliL3TrackSegmentData*)tmpP;
  }
  return size;
}

void AliL3TrackArray::AddTracks(AliL3TrackArray *newtrack){
  if(GetTrackType() != newtrack->GetTrackType())
    return;
  if(fSize < fNTracks+newtrack->GetNPresent())
    SetSize(fSize+newtrack->GetSize());
  for(Int_t i =0;i<newtrack->GetNTracks();i++){
    AliL3Track *tpt = newtrack->GetCheckedTrack(i);
    if(!tpt) continue;
    newtrack->Remove(i);
    AliL3Track *track = NextTrack();

    track->Set(tpt);

  }
}

void AliL3TrackArray::Compress(){
  if(GetNPresent()==GetNTracks()) return;
  AliL3Track **tmp =  new AliL3Track *[fNTracks];
  Int_t present=0;
  Int_t absent=GetNPresent();
  for(Int_t i=0;i<GetNTracks();i++){
    if(fIsPresent[i]) tmp[present++] = fTrack[i];
    else tmp[absent++] = fTrack[i];
  }
  for(Int_t i=0;i<GetNTracks();i++)
    fIsPresent[i]=kTRUE;

  //Copy pointers back
  for(Int_t i=0; i<GetNTracks();i++){
    fTrack[i]=tmp[i];
  }

  delete[] tmp;

  fNTracks = GetNPresent();
  fNAbsent = 0;
}

void AliL3TrackArray::QSort(){
  // compress an sort
  Compress();
  QSort(fTrack,0,fNTracks);
}

 void AliL3TrackArray::QSort( AliL3Track **a, Int_t first, Int_t last){

   // Sort array of AliL3Track pointers using a quicksort algorithm.
   // Uses TrackCompare() to compare objects.
   // Thanks to Root!

   static AliL3Track *tmp;
   static int i;           // "static" to save stack space
   int j;

   while (last - first > 1) {
      i = first;
      j = last;
      for (;;) {
         while (++i < last && TrackCompare(a[i], a[first]) < 0)
            ;
         while (--j > first && TrackCompare(a[j], a[first]) > 0)
            ;
         if (i >= j)
            break;

         tmp  = a[i];
         a[i] = a[j];
         a[j] = tmp;
      }
      if (j == first) {
         ++first;
         continue;
      }
      tmp = a[first];
      a[first] = a[j];
      a[j] = tmp;
      if (j - first < last - (j + 1)) {
         QSort(a, first, j);
         first = j + 1;   // QSort(j + 1, last);
      } else {
         QSort(a, j + 1, last);
         last = j;        // QSort(first, j);
      }
   }
}

Int_t AliL3TrackArray::TrackCompare(AliL3Track *a, AliL3Track *b){
   // Compare the two tracks.

  if(a->GetNHits() < b->GetNHits()) return 1;
  if(a->GetNHits() > b->GetNHits()) return -1;
  return 0;
}

<commit_msg>Bugfix in SetSize<commit_after>//Author:        Uli Frankenfeld
//Last Modified: 06.12.2000

#include <math.h>
#include <string.h>
#include <iostream.h>
#include "AliL3Logging.h"
#include "AliL3TrackArray.h"
#include "AliL3HoughTrack.h"
#include "AliL3ConfMapTrack.h"
#include "AliL3TrackSegmentData.h"
#include "AliL3Transform.h"
#include "AliL3ConfMapPoint.h"
//_____________________________________________________________
//
// The L3 TrackArray
//

ClassImp(AliL3TrackArray)

AliL3TrackArray::AliL3TrackArray(){
  //Default constructor
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  fTrackType='t';
  SetSize();
}

AliL3TrackArray::AliL3TrackArray(Int_t ntrack){
  //Constructor.
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  fTrackType='t';
  SetSize(ntrack);
}

AliL3TrackArray::AliL3TrackArray(char* tracktype,Int_t ntrack){
  //Constructor.
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  if(strcmp(tracktype,"AliL3Track")==0) fTrackType='t';
  if(strcmp(tracktype,"AliL3ConfMapTrack")==0) fTrackType='c';
  if(strcmp(tracktype,"AliL3HoughTrack")==0) fTrackType='h';
  SetSize(ntrack);
}

AliL3TrackArray::AliL3TrackArray(char* tracktype){
  //Constructor.
  fSize = 0;
  fNTracks=0;
  fNAbsent=0;
  if(strcmp(tracktype,"AliL3Track")==0) fTrackType='t';
  if(strcmp(tracktype,"AliL3ConfMapTrack")==0) fTrackType='c';
  if(strcmp(tracktype,"AliL3HoughTrack")==0) fTrackType='h';
  SetSize();
}

AliL3TrackArray::~AliL3TrackArray(){
  //Destructor
  DeleteArray();
}

AliL3Track *AliL3TrackArray::NextTrack(){
  if(fNTracks<fSize) return fTrack[fNTracks++];
  SetSize(fSize+100);
   return fTrack[fNTracks++];
}

void AliL3TrackArray::DeleteArray(){
  for(Int_t i=0; i<fSize;i++)
    delete fTrack[i];
  delete[] fIsPresent;
  delete[] fTrack;
}

Bool_t AliL3TrackArray::SetSize(Int_t newsize){
  if(newsize<=fSize) return kFALSE; //shrink comes later!!
  if(!fSize){
    fSize = newsize;
    fTrack = new AliL3Track*[fSize];
    fIsPresent = new Bool_t[fSize];
    switch(fTrackType){
      case 't':
        for(Int_t i=0;i<fSize;i++){
          fTrack[i]   = new AliL3Track();
          fIsPresent[i] = kTRUE;
        }
        break;
      case 'c':
        for(Int_t i=0;i<fSize;i++){
          fTrack[i]   = new AliL3ConfMapTrack();
          fIsPresent[i] = kTRUE;
        }
        break;
      case 'h':
        for(Int_t i=0;i<fSize;i++){
          fTrack[i]   = new AliL3HoughTrack();
          fIsPresent[i] = kTRUE;
        }
        break;
      default:
        return kFALSE;
    }
    return kTRUE;
  }
  AliL3Track **tmp = new AliL3Track*[fSize];
  Bool_t *pre = new Bool_t[fSize];
  for(Int_t i=0; i<fSize;i++){
    tmp[i] = fTrack[i];
    pre[i] = fIsPresent[i];
  }
  delete[]  fTrack;
  delete[] fIsPresent;
  fTrack =  new AliL3Track*[newsize];
  fIsPresent = new Bool_t[newsize];
  for(Int_t i=0; i<fSize;i++){
    fTrack[i]   = tmp[i];
    fIsPresent[i] = pre[i];
  }
  delete[] tmp;
  delete[] pre;
  switch(fTrackType){
    case 't':
      for(Int_t i=fSize;i<newsize;i++){
        fTrack[i]   = new AliL3Track();
        fIsPresent[i] = kTRUE;
      }
      break;
    case 'c':
      for(Int_t i=fSize;i<newsize;i++){
        fTrack[i]   = new AliL3ConfMapTrack();
        fIsPresent[i] = kTRUE;
      }
      break;
    case 'h':
      for(Int_t i=fSize;i<newsize;i++){
        fTrack[i]   = new AliL3HoughTrack();
        fIsPresent[i] = kTRUE;
      }
      break;
    default:
      return kFALSE;
  }
  fSize = newsize;
  return kTRUE;
}

void AliL3TrackArray::Reset(){
  fNTracks=0;
  fNAbsent=0;
  for(Int_t i=0; i<fSize;i++)
    fIsPresent[i] = kTRUE;
}

void AliL3TrackArray::Remove(Int_t track){
  if(fIsPresent[track]){
    fIsPresent[track]=kFALSE;
    fNAbsent++;
  }
}

void AliL3TrackArray::FillTracks(Int_t ntracks, AliL3TrackSegmentData* tr){
  //Read tracks from shared memory (or memory)
  AliL3TrackSegmentData *trs = tr;
  for(Int_t i=0; i<ntracks; i++){
    AliL3Track *track = NextTrack();
    track->SetPt(trs->fPt);
    track->SetPsi(trs->fPsi);
    track->SetTgl(trs->fTgl);
    track->SetNHits(trs->fNPoints);
    track->SetCharge(trs->fCharge);
    track->SetFirstPoint(trs->fX,trs->fY,trs->fZ);
    track->SetLastPoint(trs->fLastX,trs->fLastY,trs->fLastZ);
    track->SetHits( trs->fNPoints, trs->fPointIDs );
    UChar_t *tmpP = (UChar_t*)trs;
    tmpP += sizeof(AliL3TrackSegmentData)+trs->fNPoints*sizeof(UInt_t);
    trs = (AliL3TrackSegmentData*)tmpP;
  }
}

void AliL3TrackArray::FillTracks(Int_t ntracks, AliL3TrackSegmentData* tr,Int_t slice, AliL3Transform* trans){
  //Read tracks from shared memory (or memory)
  AliL3TrackSegmentData *trs = tr;
  for(Int_t i=0; i<ntracks; i++){
    AliL3Track *track = NextTrack();
    track->SetPt(trs->fPt);
    Float_t psi[1];
    psi[0]=trs->fPsi;
    trans->Local2GlobalAngle(psi,slice);
    track->SetPsi(psi[0]);
    track->SetTgl(trs->fTgl);
    track->SetNHits(trs->fNPoints);
    track->SetCharge(trs->fCharge);
    Float_t first[3];
    first[0]=trs->fX;first[1]=trs->fY;first[2]=trs->fZ;
    trans->Local2Global(first,slice);
    track->SetFirstPoint(first[0],first[1],first[2]);
    Float_t last[3];
    last[0]=trs->fLastX;last[1]=trs->fLastY;last[2]=trs->fLastZ;
    trans->Local2Global(last,slice);
    track->SetLastPoint(last[0],last[1],last[2]);
    track->SetHits( trs->fNPoints, trs->fPointIDs );
    UChar_t *tmpP = (UChar_t*)trs;
    tmpP += sizeof(AliL3TrackSegmentData)+trs->fNPoints*sizeof(UInt_t);
    trs = (AliL3TrackSegmentData*)tmpP;
  }
}

UInt_t AliL3TrackArray::GetOutSize(){
  UInt_t count = GetOutCount();   //use only present tracks
  UInt_t tHits = 0;
  for(Int_t i=0;i<fNTracks;i++){  //loop over all tracks
    AliL3Track *track = GetCheckedTrack(i);  //use only present tracks
    if(track)                                       //use only present tracks
      tHits += track->GetNHits();
  }

  //calculate size of track
  return count*sizeof(AliL3TrackSegmentData)+sizeof(UInt_t)*tHits;
}

UInt_t AliL3TrackArray::WriteTracks(UInt_t & ntracks,AliL3TrackSegmentData* tr){
  ntracks = GetOutCount();
  return WriteTracks(tr);
}

UInt_t AliL3TrackArray::WriteTracks(AliL3TrackSegmentData* tr){
  if(GetTrackType()=='c') return WriteConfMapTracks(tr);
  AliL3TrackSegmentData *tP = tr;
  UInt_t *pP;
  UInt_t size = 0;
  for(Int_t i=0; i<fNTracks; i++){  //loop over all tracks
    AliL3Track *track = GetCheckedTrack(i); //use only present tracks
    if(!track) continue;                           //use only present tracks
    tP->fX = track->GetFirstPointX();
    tP->fY = track->GetFirstPointY();
    tP->fZ = track->GetFirstPointZ();
    tP->fPt = track->GetPt();
    tP->fLastX = track->GetLastPointX();
    tP->fLastY = track->GetLastPointY();
    tP->fLastZ = track->GetLastPointZ();
    tP->fPsi = track->GetPsi();
    tP->fTgl = track->GetTgl();
    tP->fCharge = track->GetCharge();
    tP->fNPoints = track->GetNHits();
    pP = (UInt_t*)track->GetHitNumbers();
    for (UInt_t j=0;j<tP->fNPoints;j++){
      tP->fPointIDs[j] = pP[j];
    }
    Byte_t *tmpP = (Byte_t *)tP;
    tmpP += sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    size += sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    tP = (AliL3TrackSegmentData*)tmpP;
  }
  return size;
}

UInt_t AliL3TrackArray::WriteConfMapTracks(AliL3TrackSegmentData* tr){
  // use first and last point objects
  AliL3TrackSegmentData *tP = tr;
  UInt_t *pP;
  UInt_t size = 0;
  for(Int_t i=0; i<fNTracks; i++){  //loop over all tracks
    AliL3ConfMapTrack *track =(AliL3ConfMapTrack *) GetCheckedTrack(i); //use only present tracks
    if(!track) continue;                           //use only present tracks
    AliL3ConfMapPoint *hit = (AliL3ConfMapPoint*)track->lastHit;
    AliL3ConfMapPoint *lastHit = (AliL3ConfMapPoint*)track->firstHit;
    tP->fX = hit->GetX();
    tP->fY = hit->GetY();
    tP->fZ = hit->GetZ();
    tP->fLastX = lastHit->GetX();
    tP->fLastY = lastHit->GetY();
    tP->fLastZ = lastHit->GetZ();

//    tP->fX = track->GetFirstPointX();
//    tP->fY = track->GetFirstPointY();
//    tP->fZ = track->GetFirstPointZ();
    tP->fPt = track->GetPt();
//    tP->fLastX = track->GetLastPointX();
//    tP->fLastY = track->GetLastPointY();
//    tP->fLastZ = track->GetLastPointZ();
    tP->fPsi = track->GetPsi();
    tP->fTgl = track->GetTgl();
    tP->fCharge = track->GetCharge();
    tP->fNPoints = track->GetNHits();
    pP = (UInt_t*)track->GetHitNumbers();
    for (UInt_t j=0;j<tP->fNPoints;j++){
      tP->fPointIDs[j] = pP[j];
    }
    Byte_t *tmpP = (Byte_t *)tP;
    tmpP += sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    size +=sizeof(AliL3TrackSegmentData)+tP->fNPoints*sizeof(UInt_t);
    tP = (AliL3TrackSegmentData*)tmpP;
  }
  return size;
}

void AliL3TrackArray::AddTracks(AliL3TrackArray *newtrack){
  if(GetTrackType() != newtrack->GetTrackType())
    return;
  if(fSize < fNTracks+newtrack->GetNPresent())
    SetSize(fSize+newtrack->GetSize());
  for(Int_t i =0;i<newtrack->GetNTracks();i++){
    AliL3Track *tpt = newtrack->GetCheckedTrack(i);
    if(!tpt) continue;
    newtrack->Remove(i);
    AliL3Track *track = NextTrack();

    track->Set(tpt);

  }
}

void AliL3TrackArray::Compress(){
  if(GetNPresent()==GetNTracks()) return;
  AliL3Track **tmp =  new AliL3Track *[fNTracks];
  Int_t present=0;
  Int_t absent=GetNPresent();
  for(Int_t i=0;i<GetNTracks();i++){
    if(fIsPresent[i]) tmp[present++] = fTrack[i];
    else tmp[absent++] = fTrack[i];
  }
  for(Int_t i=0;i<GetNTracks();i++)
    fIsPresent[i]=kTRUE;

  //Copy pointers back
  for(Int_t i=0; i<GetNTracks();i++){
    fTrack[i]=tmp[i];
  }

  delete[] tmp;

  fNTracks = GetNPresent();
  fNAbsent = 0;
}

void AliL3TrackArray::QSort(){
  // compress an sort
  Compress();
  QSort(fTrack,0,fNTracks);
}

 void AliL3TrackArray::QSort( AliL3Track **a, Int_t first, Int_t last){

   // Sort array of AliL3Track pointers using a quicksort algorithm.
   // Uses TrackCompare() to compare objects.
   // Thanks to Root!

   static AliL3Track *tmp;
   static int i;           // "static" to save stack space
   int j;

   while (last - first > 1) {
      i = first;
      j = last;
      for (;;) {
         while (++i < last && TrackCompare(a[i], a[first]) < 0)
            ;
         while (--j > first && TrackCompare(a[j], a[first]) > 0)
            ;
         if (i >= j)
            break;

         tmp  = a[i];
         a[i] = a[j];
         a[j] = tmp;
      }
      if (j == first) {
         ++first;
         continue;
      }
      tmp = a[first];
      a[first] = a[j];
      a[j] = tmp;
      if (j - first < last - (j + 1)) {
         QSort(a, first, j);
         first = j + 1;   // QSort(j + 1, last);
      } else {
         QSort(a, j + 1, last);
         last = j;        // QSort(first, j);
      }
   }
}

Int_t AliL3TrackArray::TrackCompare(AliL3Track *a, AliL3Track *b){
   // Compare the two tracks.

  if(a->GetNHits() < b->GetNHits()) return 1;
  if(a->GetNHits() > b->GetNHits()) return -1;
  return 0;
}

<|endoftext|>


<commit_before>/**
 * \file
 * \brief requestFunctionExecution() implementation for ARMv7-M (Cortex-M3 / Cortex-M4)
 *
 * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-06-03
 */

#include "distortos/architecture/requestFunctionExecution.hpp"

#include "ExceptionFpuStackFrame.hpp"
#include "supervisorCall.hpp"

#include "distortos/scheduler/Scheduler.hpp"
#include "distortos/scheduler/getScheduler.hpp"

#include <cstring>

namespace distortos
{

namespace architecture
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Removes stack frame inserted by requestFunctionExecution() in case of interrupt -> current thread request.
 *
 * \param [in] savedStackPointer is the stack pointer value before new stack frame was created
 * \param [in] fpuContextActive (only when FPU is enabled) tells whether thread has active FPU context (true) or not
 * (false)
 */

#if __FPU_PRESENT == 1 && __FPU_USED == 1
void removeStackFrame(const void* const savedStackPointer, const bool fpuContextActive)
#else
void removeStackFrame(const void* const savedStackPointer)
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1

	if (fpuContextActive == true)
		asm volatile ("vmov s0, s0");	// force stacking of FPU context

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	__set_PSP(reinterpret_cast<uint32_t>(savedStackPointer));
}

/**
 * \brief Trampoline used to execute function on new stack frame.
 *
 * After the function returns stack frame is removed and old stack pointer value is restored.
 *
 * \param [in] function is a reference to function that will be executed
 * \param [in] savedStackPointer is the stack pointer value before new stack frame was created
 * \param [in] fullContext is an information about type of previous stack frame - "full", when the thread was not
 * running (true) or "interrupt", when the thread was running and was interrupted (false)
 */

__attribute__ ((naked))
void functionTrampoline(void (& function)(), const void* const savedStackPointer, const bool fullContext)
{
	using SupervisorCall = int(int (&)(int, int, int, int), int, int, int, int);	// type of supervisorCall()

	asm volatile
	(
#if __FPU_PRESENT == 1 && __FPU_USED == 1
			"	mrs			r3, CONTROL					\n"	// save current value of CONTROL register
			"	push		{r0-r3}						\n"	// push all arguments and value of CONTROL register to stack
#else
			"	push		{r1-r2}						\n"	// push last two arguments to stack
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
			"	blx			%[function]					\n"	// execute function
#if __FPU_PRESENT == 1 && __FPU_USED == 1
			// restore all arguments and value of CONTROL register, don't update SP
			"	ldm			sp, {r0-r3}					\n"
			// restore previous value of CONTROL register, possibly deactivating FPU context
			"	msr			CONTROL, r3					\n"
#else
			"	ldm			sp, {r1-r2}					\n"	// restore last two arguments, don't update SP
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
			"	cmp			r2, #0						\n"
#if __FPU_PRESENT == 1 && __FPU_USED == 1
			"	itt			eq							\n"	// if (fullContext == false) {
			// 3rd supervisorCall() argument - extracted CONTROL.FPCA, 1 if FPU context is active, 0 otherwise
			"	ubfxeq		r2, r3, #2, #1				\n"
			"	beq			1f							\n"	// } else {
			"	ldmia		r1!, {r4-r12, lr}			\n"	// load "regular" context of thread
			"	ubfx		r2, lr, #4, #1				\n"	// was floating-point used by the thread?
			// 3rd supervisorCall() argument - 1 if FPU context is active, 0 otherwise
			"	eors		r2, #(1 << 0)				\n"
			"	it			ne							\n"
			"	vldmiane	r1!, {s16-s31}				\n"	// load "floating-point" context of thread
			"1:											\n"	// }
#else
			"	itt			ne							\n"	// if (fullContext == true) {
			"	ldmiane		r1!, {r4-r11}				\n"	// load context of thread
			"	movne		r2, #0						\n"	// 3rd supervisorCall() argument - 0
			"											\n"	// }
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
			"	mov			r3, #0						\n"	// 4th supervisorCall() argument - 0
			"	str			r3, [sp]					\n"	// 5th supervisorCall() argument - 0
			"	ldr			r0, =%[removeStackFrame]	\n"	// 1st supervisorCall() argument - removeStackFrame
			"	b			%[supervisorCall]			\n"	// jump to supervisorCall(), this does not return

			::	[function] "r" (function),
				[savedStackPointer] "r" (savedStackPointer),
				[fullContext] "r" (fullContext),
				[removeStackFrame] "i" (removeStackFrame),
				[supervisorCall] "i" (static_cast<SupervisorCall&>(supervisorCall))
	);

	__builtin_unreachable();
}

/**
 * \brief Handles request from current thread to itself.
 *
 * \param [in] function is a reference to function that should be executed in current thread
 */

void fromCurrentThreadToCurrentThread(void (& function)())
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1
	const auto control = __get_CONTROL();
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	function();	// execute function right away

#if __FPU_PRESENT == 1 && __FPU_USED == 1
	__set_CONTROL(control);	// restore previous value of CONTROL register, possibly deactivating FPU context
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
}

/**
 * \brief Handles request coming from interrupt context to execute provided function in current thread.
 *
 * \param [in] function is a reference to function that should be executed in current thread
 */

void fromInterruptToCurrentThread(void (& function)())
{
	const auto stackPointer = __get_PSP();
	// it's not possible to know whether the thread has active FPU context, so the only option is to assume it does;
	// ExceptionFpuStackFrame is equal to ExceptionStackFrame in case of compilation with disabled FPU, so no memory is
	// wasted in that case
	const auto exceptionFpuStackFrame = reinterpret_cast<ExceptionFpuStackFrame*>(stackPointer) - 1;

#if __FPU_PRESENT == 1 && __FPU_USED == 1

	const auto fpccr = FPU->FPCCR;
	// last FPU stack frame was allocated in thread mode and the stacking is still pending?
	// this condition will be false in following situations:
	// - thread doesn't use FPU - there was no FPU stack frame allocated in thread mode,
	// - thread uses FPU, but the registers are already stacked.
	if ((fpccr & FPU_FPCCR_THREAD_Msk) != 0 && (fpccr & FPU_FPCCR_LSPACT_Msk) != 0)
		asm volatile ("vmov s0, s0");	// force stacking of FPU context

	memset(exceptionFpuStackFrame, 0, sizeof(*exceptionFpuStackFrame));
	exceptionFpuStackFrame->fpscr = reinterpret_cast<void*>(FPU->FPDSCR);

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	exceptionFpuStackFrame->exceptionStackFrame.r0 = reinterpret_cast<void*>(&function);
	exceptionFpuStackFrame->exceptionStackFrame.r1 = reinterpret_cast<void*>(stackPointer);
	exceptionFpuStackFrame->exceptionStackFrame.r2 = reinterpret_cast<void*>(false);
	exceptionFpuStackFrame->exceptionStackFrame.r3 = reinterpret_cast<void*>(0x33333333);
	exceptionFpuStackFrame->exceptionStackFrame.r12 = reinterpret_cast<void*>(0xcccccccc);
	exceptionFpuStackFrame->exceptionStackFrame.lr = nullptr;
	exceptionFpuStackFrame->exceptionStackFrame.pc = reinterpret_cast<void*>(&functionTrampoline);
	exceptionFpuStackFrame->exceptionStackFrame.xpsr = ExceptionStackFrame::defaultXpsr;

	__set_PSP(reinterpret_cast<uint32_t>(exceptionFpuStackFrame));
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global functions
+---------------------------------------------------------------------------------------------------------------------*/

void requestFunctionExecution(scheduler::ThreadControlBlock& threadControlBlock, void (& function)())
{
	const auto& currentThreadControlBlock = scheduler::getScheduler().getCurrentThreadControlBlock();
	if (&threadControlBlock == &currentThreadControlBlock)	// request to current thread?
	{
		const auto inInterrupt = __get_IPSR() != 0;
		if (inInterrupt == false)	// current thread is sending the request to itself?
			fromCurrentThreadToCurrentThread(function);
		else						// interrupt is sending the request to current thread?
			fromInterruptToCurrentThread(function);
	}
}

}	// namespace architecture

}	// namespace distortos
<commit_msg>architecture/ARM/ARMv7-M, requestFunctionExecution(): add support for requests to non-current thread<commit_after>/**
 * \file
 * \brief requestFunctionExecution() implementation for ARMv7-M (Cortex-M3 / Cortex-M4)
 *
 * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2015-06-03
 */

#include "distortos/architecture/requestFunctionExecution.hpp"

#include "ExceptionFpuStackFrame.hpp"
#include "StackFrame.hpp"
#include "supervisorCall.hpp"

#include "distortos/scheduler/Scheduler.hpp"
#include "distortos/scheduler/getScheduler.hpp"

#include <cstring>

namespace distortos
{

namespace architecture
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief Removes stack frame inserted by requestFunctionExecution() in case of interrupt -> current thread request.
 *
 * \param [in] savedStackPointer is the stack pointer value before new stack frame was created
 * \param [in] fpuContextActive (only when FPU is enabled) tells whether thread has active FPU context (true) or not
 * (false)
 */

#if __FPU_PRESENT == 1 && __FPU_USED == 1
void removeStackFrame(const void* const savedStackPointer, const bool fpuContextActive)
#else
void removeStackFrame(const void* const savedStackPointer)
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1

	if (fpuContextActive == true)
		asm volatile ("vmov s0, s0");	// force stacking of FPU context

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	__set_PSP(reinterpret_cast<uint32_t>(savedStackPointer));
}

/**
 * \brief Trampoline used to execute function on new stack frame.
 *
 * After the function returns stack frame is removed and old stack pointer value is restored.
 *
 * \param [in] function is a reference to function that will be executed
 * \param [in] savedStackPointer is the stack pointer value before new stack frame was created
 * \param [in] fullContext is an information about type of previous stack frame - "full", when the thread was not
 * running (true) or "interrupt", when the thread was running and was interrupted (false)
 */

__attribute__ ((naked))
void functionTrampoline(void (& function)(), const void* const savedStackPointer, const bool fullContext)
{
	using SupervisorCall = int(int (&)(int, int, int, int), int, int, int, int);	// type of supervisorCall()

	asm volatile
	(
#if __FPU_PRESENT == 1 && __FPU_USED == 1
			"	mrs			r3, CONTROL					\n"	// save current value of CONTROL register
			"	push		{r0-r3}						\n"	// push all arguments and value of CONTROL register to stack
#else
			"	push		{r1-r2}						\n"	// push last two arguments to stack
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
			"	blx			%[function]					\n"	// execute function
#if __FPU_PRESENT == 1 && __FPU_USED == 1
			// restore all arguments and value of CONTROL register, don't update SP
			"	ldm			sp, {r0-r3}					\n"
			// restore previous value of CONTROL register, possibly deactivating FPU context
			"	msr			CONTROL, r3					\n"
#else
			"	ldm			sp, {r1-r2}					\n"	// restore last two arguments, don't update SP
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
			"	cmp			r2, #0						\n"
#if __FPU_PRESENT == 1 && __FPU_USED == 1
			"	itt			eq							\n"	// if (fullContext == false) {
			// 3rd supervisorCall() argument - extracted CONTROL.FPCA, 1 if FPU context is active, 0 otherwise
			"	ubfxeq		r2, r3, #2, #1				\n"
			"	beq			1f							\n"	// } else {
			"	ldmia		r1!, {r4-r12, lr}			\n"	// load "regular" context of thread
			"	ubfx		r2, lr, #4, #1				\n"	// was floating-point used by the thread?
			// 3rd supervisorCall() argument - 1 if FPU context is active, 0 otherwise
			"	eors		r2, #(1 << 0)				\n"
			"	it			ne							\n"
			"	vldmiane	r1!, {s16-s31}				\n"	// load "floating-point" context of thread
			"1:											\n"	// }
#else
			"	itt			ne							\n"	// if (fullContext == true) {
			"	ldmiane		r1!, {r4-r11}				\n"	// load context of thread
			"	movne		r2, #0						\n"	// 3rd supervisorCall() argument - 0
			"											\n"	// }
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
			"	mov			r3, #0						\n"	// 4th supervisorCall() argument - 0
			"	str			r3, [sp]					\n"	// 5th supervisorCall() argument - 0
			"	ldr			r0, =%[removeStackFrame]	\n"	// 1st supervisorCall() argument - removeStackFrame
			"	b			%[supervisorCall]			\n"	// jump to supervisorCall(), this does not return

			::	[function] "r" (function),
				[savedStackPointer] "r" (savedStackPointer),
				[fullContext] "r" (fullContext),
				[removeStackFrame] "i" (removeStackFrame),
				[supervisorCall] "i" (static_cast<SupervisorCall&>(supervisorCall))
	);

	__builtin_unreachable();
}

/**
 * \brief Handles request from current thread to itself.
 *
 * \param [in] function is a reference to function that should be executed in current thread
 */

void fromCurrentThreadToCurrentThread(void (& function)())
{
#if __FPU_PRESENT == 1 && __FPU_USED == 1
	const auto control = __get_CONTROL();
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	function();	// execute function right away

#if __FPU_PRESENT == 1 && __FPU_USED == 1
	__set_CONTROL(control);	// restore previous value of CONTROL register, possibly deactivating FPU context
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1
}

/**
 * \brief Handles request coming from interrupt context to execute provided function in current thread.
 *
 * \param [in] function is a reference to function that should be executed in current thread
 */

void fromInterruptToCurrentThread(void (& function)())
{
	const auto stackPointer = __get_PSP();
	// it's not possible to know whether the thread has active FPU context, so the only option is to assume it does;
	// ExceptionFpuStackFrame is equal to ExceptionStackFrame in case of compilation with disabled FPU, so no memory is
	// wasted in that case
	const auto exceptionFpuStackFrame = reinterpret_cast<ExceptionFpuStackFrame*>(stackPointer) - 1;

#if __FPU_PRESENT == 1 && __FPU_USED == 1

	const auto fpccr = FPU->FPCCR;
	// last FPU stack frame was allocated in thread mode and the stacking is still pending?
	// this condition will be false in following situations:
	// - thread doesn't use FPU - there was no FPU stack frame allocated in thread mode,
	// - thread uses FPU, but the registers are already stacked.
	if ((fpccr & FPU_FPCCR_THREAD_Msk) != 0 && (fpccr & FPU_FPCCR_LSPACT_Msk) != 0)
		asm volatile ("vmov s0, s0");	// force stacking of FPU context

	memset(exceptionFpuStackFrame, 0, sizeof(*exceptionFpuStackFrame));
	exceptionFpuStackFrame->fpscr = reinterpret_cast<void*>(FPU->FPDSCR);

#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	exceptionFpuStackFrame->exceptionStackFrame.r0 = reinterpret_cast<void*>(&function);
	exceptionFpuStackFrame->exceptionStackFrame.r1 = reinterpret_cast<void*>(stackPointer);
	exceptionFpuStackFrame->exceptionStackFrame.r2 = reinterpret_cast<void*>(false);
	exceptionFpuStackFrame->exceptionStackFrame.r3 = reinterpret_cast<void*>(0x33333333);
	exceptionFpuStackFrame->exceptionStackFrame.r12 = reinterpret_cast<void*>(0xcccccccc);
	exceptionFpuStackFrame->exceptionStackFrame.lr = nullptr;
	exceptionFpuStackFrame->exceptionStackFrame.pc = reinterpret_cast<void*>(&functionTrampoline);
	exceptionFpuStackFrame->exceptionStackFrame.xpsr = ExceptionStackFrame::defaultXpsr;

	__set_PSP(reinterpret_cast<uint32_t>(exceptionFpuStackFrame));
}

/**
 * \brief Handles request to execute provided function in non-current thread.
 *
 * \param [in] threadControlBlock is a reference to scheduler::ThreadControlBlock of thread in which \a function should
 * be executed
 * \param [in] function is a reference to function that should be executed in thread associated with
 * \a threadControlBlock
 */

void toNonCurrentThread(scheduler::ThreadControlBlock& threadControlBlock, void (& function)())
{
	const auto stackPointer = threadControlBlock.getStack().getStackPointer();
	const auto stackFrame = reinterpret_cast<StackFrame*>(stackPointer) - 1;

	stackFrame->softwareStackFrame.r4 = reinterpret_cast<void*>(0x44444444);
	stackFrame->softwareStackFrame.r5 = reinterpret_cast<void*>(0x55555555);
	stackFrame->softwareStackFrame.r6 = reinterpret_cast<void*>(0x66666666);
	stackFrame->softwareStackFrame.r7 = reinterpret_cast<void*>(0x77777777);
	stackFrame->softwareStackFrame.r8 = reinterpret_cast<void*>(0x88888888);
	stackFrame->softwareStackFrame.r9 = reinterpret_cast<void*>(0x99999999);
	stackFrame->softwareStackFrame.r10 = reinterpret_cast<void*>(0xaaaaaaaa);
	stackFrame->softwareStackFrame.r11 = reinterpret_cast<void*>(0xbbbbbbbb);
#if __FPU_PRESENT == 1 && __FPU_USED == 1
	stackFrame->softwareStackFrame.exceptionReturn = SoftwareStackFrame::defaultExceptionReturn;
#endif	// __FPU_PRESENT == 1 && __FPU_USED == 1

	stackFrame->exceptionStackFrame.r0 = reinterpret_cast<void*>(&function);
	stackFrame->exceptionStackFrame.r1 = reinterpret_cast<void*>(stackPointer);
	stackFrame->exceptionStackFrame.r2 = reinterpret_cast<void*>(true);
	stackFrame->exceptionStackFrame.r3 = reinterpret_cast<void*>(0x33333333);
	stackFrame->exceptionStackFrame.r12 = reinterpret_cast<void*>(0xcccccccc);
	stackFrame->exceptionStackFrame.lr = nullptr;
	stackFrame->exceptionStackFrame.pc = reinterpret_cast<void*>(&functionTrampoline);
	stackFrame->exceptionStackFrame.xpsr = ExceptionStackFrame::defaultXpsr;

	threadControlBlock.getStack().setStackPointer(stackFrame);
}

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global functions
+---------------------------------------------------------------------------------------------------------------------*/

void requestFunctionExecution(scheduler::ThreadControlBlock& threadControlBlock, void (& function)())
{
	const auto& currentThreadControlBlock = scheduler::getScheduler().getCurrentThreadControlBlock();
	if (&threadControlBlock == &currentThreadControlBlock)	// request to current thread?
	{
		const auto inInterrupt = __get_IPSR() != 0;
		if (inInterrupt == false)	// current thread is sending the request to itself?
			fromCurrentThreadToCurrentThread(function);
		else						// interrupt is sending the request to current thread?
			fromInterruptToCurrentThread(function);
	}
	else	// request to non-current thread
		toNonCurrentThread(threadControlBlock, function);
}

}	// namespace architecture

}	// namespace distortos
<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2017 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <pybind11/pybind11.h>
#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/util/filesystem.h>
#include <modules/python3/pythoninterpreter.h>

#include <modules/python3/python3module.h>
#include <inviwo/core/util/filesystem.h>

#include <modules/python3/pythonscript.h>
#include <modules/python3/pythonexecutionoutputobservable.h>

#if defined(__unix__)
#include <unistd.h>
#elif defined(__APPLE__)
#include <libproc.h>  // proc_pidpath
#endif

namespace inviwo {

static PyObject* py_stdout(PyObject* /*self*/, PyObject* args) {
    char* msg;
    int len;
    int isStderr;

    if (!PyArg_ParseTuple(args, "s#i", &msg, &len, &isStderr)) {
        LogWarnCustom("inviwo.Python.py_print", "failed to parse log message");
    } else {
        if (len != 0) {
            if (!(len == 1 && (msg[0] == '\n' || msg[0] == '\r' || msg[0] == '\0')))
                if (auto app = InviwoApplication::getPtr()) {
                    if (auto module = app->getModuleByType<Python3Module>()) {
                        if (auto interpreter = module->getPythonInterpreter()) {
                            interpreter->pythonExecutionOutputEvent(
                                msg, (isStderr == 0) ? sysstdout : sysstderr);
                        }
                    }
                }
        }
    }

    Py_RETURN_NONE;
}

PythonInterpreter::PythonInterpreter(Python3Module* module) : isInit_(false) { init(module); }

PythonInterpreter::~PythonInterpreter() { Py_Finalize(); }

void PythonInterpreter::addModulePath(const std::string& path) {
    if (!Py_IsInitialized()) {
        LogWarn("addModulePath(): Python is not initialized");
        return;
    }

    std::string pathConv = path;
    replaceInString(pathConv, "\\", "/");
    std::string code = "import sys\n";
    code.append(std::string("sys.path.append('") + pathConv + std::string("')"));
    if (!runString(code)) {
        LogWarn("Failed to add '" + pathConv + "' to Python module search path");
    }
}

#include <warn/push>
#include <warn/ignore/missing-field-initializers>

static PyMethodDef Inviwo_Internals_METHODS[] = {
    {"ivwPrint", py_stdout, METH_VARARGS, "A simple example of an embedded function."}, nullptr};

#include <warn/pop>

struct PyModuleDef Inviwo_Internals_Module_Def = {PyModuleDef_HEAD_INIT,
                                                  "inviwo_internal",
                                                  nullptr,
                                                  -1,
                                                  Inviwo_Internals_METHODS,
                                                  nullptr,
                                                  nullptr,
                                                  nullptr,
                                                  nullptr};

void PythonInterpreter::init(Python3Module* module) {
    if (isInit_) return;

    isInit_ = true;
    LogInfo("Python version: " + toString(Py_GetVersion()));
    wchar_t programName[] = L"PyInviwo";
    Py_SetProgramName(programName);

    Py_InitializeEx(false);

    if (!Py_IsInitialized()) {
        LogError("Python is not Initialized");
        return;
    }

    PyEval_InitThreads();
    importModule("builtins");
    importModule("sys");

    dict_ = PyImport_GetModuleDict();

    PyObject* internalModule = PyModule_Create(&Inviwo_Internals_Module_Def);
    if (!internalModule) {
        LogError("Failed to init inviwo_internal");
    } else {
        PyDict_SetItemString(dict_, "inviwo_internal", internalModule);
    }

    addModulePath(module->getPath() + "/scripts");
    initOutputRedirector(module);

#if defined(__unix__)
    char executablePath[PATH_MAX];
    auto size = ::readlink("/proc/self/exe", executablePath, sizeof(executablePath) - 1);
    if (size != -1) {
        // readlink does not append a null character to the path
        executablePath[size] = '\0';
    } else {
        // Error retrieving path
        executablePath[0] = '\0';
    }

    std::string execpath(executablePath);
    auto folder = filesystem::getFileDirectory(execpath);
    addModulePath(folder);
#elif defined(__APPLE__)
    // http://stackoverflow.com/questions/799679/programatically-retrieving-the-absolute-path-of-an-os-x-command-line-app/1024933#1024933
    char executablePath[PATH_MAX];
    auto pid = getpid();
    if (proc_pidpath(pid, executablePath, sizeof(executablePath)) <= 0) {
        // Error retrieving path
        executablePath[0] = '\0';
    }
    std::string execpath(executablePath);
    auto folder = filesystem::getFileDirectory(execpath);
    addModulePath(folder);
    addModulePath(folder + "/../../../");

#endif
}

void PythonInterpreter::importModule(const std::string& moduleName) {
    auto mainDict = PyImport_GetModuleDict();
    const static std::string __key__ = "__";
    char* key = new char[moduleName.size() + 5];
    sprintf(key, "__%s__", moduleName.c_str());
    if (PyDict_GetItemString(mainDict, key) == nullptr) {
        PyObject* pMod = PyImport_ImportModule(moduleName.c_str());
        if (nullptr != pMod) {
            PyDict_SetItemString(mainDict, key, pMod);
        } else {
            LogWarn("Failed to import python module: " << moduleName);
        }
    }
    delete[] key;
}

bool PythonInterpreter::runString(std::string code) {
    auto ret = PyRun_SimpleString(code.c_str());
    return ret == 0;
}

void PythonInterpreter::initOutputRedirector(Python3Module* module) {
    std::string directorFileName = module->getPath() + "/scripts/outputredirector.py";

    if (!filesystem::fileExists(directorFileName)) {
        LogError("Could not open outputredirector.py");
        return;
    }

    PythonScriptDisk outputCatcher(directorFileName);
    if (!outputCatcher.run()) {
        LogWarn("Python init script failed to run");
    }
}

}  // namespace
<commit_msg>Python3: Cleaned up the getting the executable path for unix and OSX (method now avaiable in core)<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2014-2017 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <pybind11/pybind11.h>
#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/util/filesystem.h>
#include <modules/python3/pythoninterpreter.h>

#include <modules/python3/python3module.h>
#include <inviwo/core/util/filesystem.h>

#include <modules/python3/pythonscript.h>
#include <modules/python3/pythonexecutionoutputobservable.h>

#if defined(__unix__)
#include <unistd.h>
#elif defined(__APPLE__)
#include <libproc.h>  // proc_pidpath
#endif

namespace inviwo {

static PyObject* py_stdout(PyObject* /*self*/, PyObject* args) {
    char* msg;
    int len;
    int isStderr;

    if (!PyArg_ParseTuple(args, "s#i", &msg, &len, &isStderr)) {
        LogWarnCustom("inviwo.Python.py_print", "failed to parse log message");
    } else {
        if (len != 0) {
            if (!(len == 1 && (msg[0] == '\n' || msg[0] == '\r' || msg[0] == '\0')))
                if (auto app = InviwoApplication::getPtr()) {
                    if (auto module = app->getModuleByType<Python3Module>()) {
                        if (auto interpreter = module->getPythonInterpreter()) {
                            interpreter->pythonExecutionOutputEvent(
                                msg, (isStderr == 0) ? sysstdout : sysstderr);
                        }
                    }
                }
        }
    }

    Py_RETURN_NONE;
}

PythonInterpreter::PythonInterpreter(Python3Module* module) : isInit_(false) { init(module); }

PythonInterpreter::~PythonInterpreter() { Py_Finalize(); }

void PythonInterpreter::addModulePath(const std::string& path) {
    if (!Py_IsInitialized()) {
        LogWarn("addModulePath(): Python is not initialized");
        return;
    }

    std::string pathConv = path;
    replaceInString(pathConv, "\\", "/");
    std::string code = "import sys\n";
    code.append(std::string("sys.path.append('") + pathConv + std::string("')"));
    if (!runString(code)) {
        LogWarn("Failed to add '" + pathConv + "' to Python module search path");
    }
}

#include <warn/push>
#include <warn/ignore/missing-field-initializers>

static PyMethodDef Inviwo_Internals_METHODS[] = {
    {"ivwPrint", py_stdout, METH_VARARGS, "A simple example of an embedded function."}, nullptr};

#include <warn/pop>

struct PyModuleDef Inviwo_Internals_Module_Def = {PyModuleDef_HEAD_INIT,
                                                  "inviwo_internal",
                                                  nullptr,
                                                  -1,
                                                  Inviwo_Internals_METHODS,
                                                  nullptr,
                                                  nullptr,
                                                  nullptr,
                                                  nullptr};

void PythonInterpreter::init(Python3Module* module) {
    if (isInit_) return;

    isInit_ = true;
    LogInfo("Python version: " + toString(Py_GetVersion()));
    wchar_t programName[] = L"PyInviwo";
    Py_SetProgramName(programName);

    Py_InitializeEx(false);

    if (!Py_IsInitialized()) {
        LogError("Python is not Initialized");
        return;
    }

    PyEval_InitThreads();
    importModule("builtins");
    importModule("sys");

    dict_ = PyImport_GetModuleDict();

    PyObject* internalModule = PyModule_Create(&Inviwo_Internals_Module_Def);
    if (!internalModule) {
        LogError("Failed to init inviwo_internal");
    } else {
        PyDict_SetItemString(dict_, "inviwo_internal", internalModule);
    }

    addModulePath(module->getPath() + "/scripts");
    initOutputRedirector(module);

#if defined(__unix__) || defined(__APPLE__)
    auto execpath = filesystem::getExecutablePath();
    auto folder = filesystem::getFileDirectory(execpath);
    addModulePath(folder);
#endif
#if defined(__APPLE__)
    // On OSX the path returned by getExecutablePath includes folder-paths inside the app-binary
    addModulePath(folder + "/../../../");
#endif
}

void PythonInterpreter::importModule(const std::string& moduleName) {
    auto mainDict = PyImport_GetModuleDict();
    const static std::string __key__ = "__";
    char* key = new char[moduleName.size() + 5];
    sprintf(key, "__%s__", moduleName.c_str());
    if (PyDict_GetItemString(mainDict, key) == nullptr) {
        PyObject* pMod = PyImport_ImportModule(moduleName.c_str());
        if (nullptr != pMod) {
            PyDict_SetItemString(mainDict, key, pMod);
        } else {
            LogWarn("Failed to import python module: " << moduleName);
        }
    }
    delete[] key;
}

bool PythonInterpreter::runString(std::string code) {
    auto ret = PyRun_SimpleString(code.c_str());
    return ret == 0;
}

void PythonInterpreter::initOutputRedirector(Python3Module* module) {
    std::string directorFileName = module->getPath() + "/scripts/outputredirector.py";

    if (!filesystem::fileExists(directorFileName)) {
        LogError("Could not open outputredirector.py");
        return;
    }

    PythonScriptDisk outputCatcher(directorFileName);
    if (!outputCatcher.run()) {
        LogWarn("Python init script failed to run");
    }
}

}  // namespace
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Intel(r) Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http ://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#ifndef __VMF_TYPES_H__
#define __VMF_TYPES_H__

/*!
* \file types.hpp
* \brief Header file contains declarations of VMF numeric types
*/

#include <cstdint>
#include <cmath>
#include <memory>
#include <cstring>
#include <utility>
#include <vector>

namespace vmf
{
    /*!
    * \brief Compares double values
    */
    static bool DOUBLE_EQ(double a, double b)
    {
        return std::abs(a - b) < std::numeric_limits<double>::epsilon();
    }

    /*!
    * \brief Character type. Equivalent of char type in C/C++
    */
    typedef char vmf_char;

    /*!
    * \brief 64-bit integer type.
    */
    typedef int64_t vmf_integer;

    /*!
    * \brief Double precesion floating point type.
    */
    typedef double vmf_real;

    /*!
    * \brief String type.
    */
    typedef std::string vmf_string;

    /*!
    * \brief floating point vector2D type.
    */
    struct vmf_vec2d
    {
	vmf_vec2d() : x(0), y(0) {}
	vmf_vec2d(vmf_real _x, vmf_real _y) : x(_x), y(_y) {}
	bool operator == (const vmf_vec2d& value) const
	{
	    return DOUBLE_EQ(x, value.x) && DOUBLE_EQ(y, value.y);
	}
	vmf_real x;
	vmf_real y;
    };

    /*!
    * \brief floating point vector3D type.
    */
    struct vmf_vec3d : vmf_vec2d
    {
	vmf_vec3d() : vmf_vec2d(), z(0){}
	vmf_vec3d(vmf_real _x, vmf_real _y, vmf_real _z) : vmf_vec2d(_x, _y), z(_z) {}
	bool operator == (const vmf_vec3d& value) const
	{
	    return vmf_vec2d::operator==(value) && DOUBLE_EQ(z, value.z);
	}
	vmf_real z;
    };

    /*!
    * \brief floating point vector4D type.
    */
    struct vmf_vec4d : vmf_vec3d
    {
	vmf_vec4d() : vmf_vec3d(), w(0){}
	vmf_vec4d(vmf_real _x, vmf_real _y, vmf_real _z, vmf_real _w) : vmf_vec3d(_x, _y, _z), w(_w) {}
	bool operator == (const vmf_vec4d& value) const
	{
	    return vmf_vec3d::operator==(value) && DOUBLE_EQ(w, value.w);
	}
	vmf_real w;
    };

    /*!
    * \brief binary buffer with arbitrary content
    */
    struct vmf_rawbuffer : public std::vector<char>
    {
    public:
        vmf_rawbuffer() : std::vector<char>()
        { }

        explicit vmf_rawbuffer(const vmf_rawbuffer& other) : std::vector<char>(other)
        { }

        vmf_rawbuffer(vmf_rawbuffer&& other)
        {
            std::swap(*this, other);
        }

        vmf_rawbuffer& operator=(const vmf_rawbuffer& other)
        {
            return static_cast<vmf_rawbuffer&>(
                   std::vector<char>::operator=(
                   static_cast< const std::vector<char> & >(other)));
        }

        vmf_rawbuffer(const char* str, const size_t len)
        {
            *this = (str != nullptr) ? vmf_rawbuffer(str, str + len) : vmf_rawbuffer(len);
        }

    private:
        // These constructors are private
        // because a Variant value can be converted to both size_t and vmf_rawbuffer
        vmf_rawbuffer(const size_t len) : std::vector<char>(len)
        { }

        vmf_rawbuffer(const char* strBegin,
                      const char* strEnd) : std::vector<char>(strBegin, strEnd)
        { }
    };

} /* vmf */
#endif /* __VMF_TYPES_H__ */
<commit_msg>move version of operator= added<commit_after>/*
 * Copyright 2015 Intel(r) Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http ://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#ifndef __VMF_TYPES_H__
#define __VMF_TYPES_H__

/*!
* \file types.hpp
* \brief Header file contains declarations of VMF numeric types
*/

#include <cstdint>
#include <cmath>
#include <memory>
#include <cstring>
#include <utility>
#include <vector>

namespace vmf
{
    /*!
    * \brief Compares double values
    */
    static bool DOUBLE_EQ(double a, double b)
    {
        return std::abs(a - b) < std::numeric_limits<double>::epsilon();
    }

    /*!
    * \brief Character type. Equivalent of char type in C/C++
    */
    typedef char vmf_char;

    /*!
    * \brief 64-bit integer type.
    */
    typedef int64_t vmf_integer;

    /*!
    * \brief Double precesion floating point type.
    */
    typedef double vmf_real;

    /*!
    * \brief String type.
    */
    typedef std::string vmf_string;

    /*!
    * \brief floating point vector2D type.
    */
    struct vmf_vec2d
    {
	vmf_vec2d() : x(0), y(0) {}
	vmf_vec2d(vmf_real _x, vmf_real _y) : x(_x), y(_y) {}
	bool operator == (const vmf_vec2d& value) const
	{
	    return DOUBLE_EQ(x, value.x) && DOUBLE_EQ(y, value.y);
	}
	vmf_real x;
	vmf_real y;
    };

    /*!
    * \brief floating point vector3D type.
    */
    struct vmf_vec3d : vmf_vec2d
    {
	vmf_vec3d() : vmf_vec2d(), z(0){}
	vmf_vec3d(vmf_real _x, vmf_real _y, vmf_real _z) : vmf_vec2d(_x, _y), z(_z) {}
	bool operator == (const vmf_vec3d& value) const
	{
	    return vmf_vec2d::operator==(value) && DOUBLE_EQ(z, value.z);
	}
	vmf_real z;
    };

    /*!
    * \brief floating point vector4D type.
    */
    struct vmf_vec4d : vmf_vec3d
    {
	vmf_vec4d() : vmf_vec3d(), w(0){}
	vmf_vec4d(vmf_real _x, vmf_real _y, vmf_real _z, vmf_real _w) : vmf_vec3d(_x, _y, _z), w(_w) {}
	bool operator == (const vmf_vec4d& value) const
	{
	    return vmf_vec3d::operator==(value) && DOUBLE_EQ(w, value.w);
	}
	vmf_real w;
    };

    /*!
    * \brief binary buffer with arbitrary content
    */
    struct vmf_rawbuffer : public std::vector<char>
    {
    public:
        vmf_rawbuffer() : std::vector<char>()
        { }

        explicit vmf_rawbuffer(const vmf_rawbuffer& other) : std::vector<char>(other)
        { }

        vmf_rawbuffer(vmf_rawbuffer&& other)
        {
            std::swap(*this, other);
        }

        vmf_rawbuffer& operator=(const vmf_rawbuffer& other)
        {
            return static_cast<vmf_rawbuffer&>(
                   std::vector<char>::operator=(
                   static_cast< const std::vector<char> & >(other)));
        }

        vmf_rawbuffer& operator=(vmf_rawbuffer&& other)
        {
            return static_cast<vmf_rawbuffer&>(
                   std::vector<char>::operator=(
                   static_cast< std::vector<char> && >(other)));
        }

        vmf_rawbuffer(const char* str, const size_t len)
        {
            *this = (str != nullptr) ? vmf_rawbuffer(str, str + len) : vmf_rawbuffer(len);
        }

    private:
        // These constructors are private
        // because a Variant value can be converted to both size_t and vmf_rawbuffer
        vmf_rawbuffer(const size_t len) : std::vector<char>(len)
        { }

        vmf_rawbuffer(const char* strBegin,
                      const char* strEnd) : std::vector<char>(strBegin, strEnd)
        { }
    };

} /* vmf */
#endif /* __VMF_TYPES_H__ */
<|endoftext|>


<commit_before>#pragma once
#ifndef HISTOGRAM_ND_HPP
#define HISTOGRAM_ND_HPP

#include <cmath>
#include <vector>
#include <functional>
#include <initializer_list>

//=========================================================================
// Example 0
//
// 2-d histogram with double precision, one dimension is int, other is float
// dimension 0 : takes integers with min 0, max 10 and 10 buckets
// dimension 1 : takes floats with min 0, max 10 and 15 buckets
//
// auto histogram2d =
//		Histogram::HistogramUniformND_<double, int, float>(
//			MinMaxBins<int>{0,10,10},
//			MinMaxBins<float>{0,10,15});
//
// histogram.Inc(5, 5.0f);
//
// auto value = histogram.Value(5, 5.0f);
//=========================================================================
// Example 1
//
// 4-d histogram with double precision, one dimension is int,
//		other is float, other is double and final is uint8_t
// dimension 0 : takes integer with min 0, max 100 and 10 buckets
// dimension 1 : takes float with min 0, max 100 and 15 buckets
// dimension 2 : takes double with min 0, max 1000 and 100 buckets
// dimension 3 : takes uint8_t with min 0, max 255 and 5 buckets
//
// auto histogram4d =
//		Histogram::HistogramUniformND_<double, int, float, double, uint8_t>(
//			MinMaxBins<int>{0,100,10},
//			MinMaxBins<float>{0,100,15},
//			MinMaxBins<double>{0,100,15},
//			MinMaxBins<uint32_t>{0,100,15},);
//
// histogram.Inc(5, 50.0f, 88.0d, 1);
//
// auto value = histogram.Value(5, 50.0f, 88.0d, 1);
//
//=========================================================================
namespace Histogram
{
	template<typename T>
	struct MinMaxBins
	{
		T min;
		T max;
		uint32_t bins;
	};

	template<typename Type>
	std::function<uint32_t(Type)>
	FactoryUniformIndexFunction1D_(
			const MinMaxBins<Type>& m)
	{
		if (m.min >= m.max)
		{
			throw std::invalid_argument("min should be < max");
		}

		if (m.bins <= 0)
		{
			throw std::invalid_argument("bins must be > 0");
		}

		return
			[m](Type value)
			{
				if (value <= m.min)
				{
					return uint32_t(0);
				}

				if (value >= m.max)
				{
					return uint32_t(m.bins - 1);
				}

				return uint32_t(std::round(
						double(value) * double(m.bins - 1) / double(m.max - m.min)));
			};
	}

	//=========================================================================

	template<typename T>
	inline uint32_t
	NumberOfBins(
			MinMaxBins<T> minMaxBin)
	{
		return minMaxBin.bins;
	}

	template<typename T, typename... Args>
	inline uint32_t
	NumberOfBins(
			MinMaxBins<T> minMaxBin,
			MinMaxBins<Args>... minMaxBins)
	{
		return minMaxBin.bins * NumberOfBins(minMaxBins...);
	}

	//=========================================================================

	template<typename T>
	inline std::function<uint32_t(T)>
	Factory(MinMaxBins<T> m0)
	{
		auto f0 = FactoryUniformIndexFunction1D_<T>(m0);
		return [f0](T t0){ return f0(t0);};
	}

	template<typename T, typename... Args>
	inline std::function<uint32_t(T, Args...)>
	Factory(MinMaxBins<T> m0, MinMaxBins<Args>... mN)
	{
		auto f0 = FactoryUniformIndexFunction1D_<T>(m0);

		return
			[f0, m0, mN...](T t0, Args... args)
			{
				return f0(t0) + m0.bins * Factory<Args...>(mN...)(args...);
			};
	}

	//=========================================================================

	template<
		typename Precision,
		typename... Types>
	class Histogram_
	{
		protected:
			std::vector<Precision> m_bins;
			std::function<uint32_t(Types...)> m_indexOf = nullptr;
		public:
			//-----------------------------------------------
			Histogram_(
					uint32_t noBins,
					std::function<uint32_t(Types...)> indexFunction)
			{
				if (noBins <= 0)
				{
					throw std::invalid_argument("bins should be > 0");
				}

				if (indexFunction == nullptr)
				{
					throw std::invalid_argument("index function should be > 0");
				}

				m_indexOf = indexFunction;
				m_bins = std::vector<Precision>(noBins, Precision(0));
			}

			//-----------------------------------------------

			Histogram_(
					const Histogram_& histogram) :
						Histogram_(histogram.Bins().size(), histogram.IndexFunction())
			{
				Set(histogram);
			}

			//-----------------------------------------------

			~Histogram_()
			{}

			//-----------------------------------------------

			/*
			 * Apply operation on bins
			 */
			Histogram_<Precision, Types...>&
			Apply(const std::function<Precision(Precision)> func)
			{
				std::for_each(
						m_bins.begin(),
						m_bins.end(),
						[func](Precision& bin)
						{
							bin = func(bin);
						});
				return *this;
			}

			//-----------------------------------------------

			Histogram_& IncMultiplier(Precision multiplier, Types... types)
			{
				auto index = m_indexOf(types...);
				m_bins[index] += multiplier;
				return *this;
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Inc(Types... types)
			{
				return IncMultiplier(Precision(1), types...);
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Set(Precision value)
			{
				return Apply([value](Precision bin){return value;});
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Set(const std::vector<Precision>& bins)
			{
				size_t mBinsSize = m_bins.size();
				size_t binsSize = bins.size();

				if (mBinsSize != binsSize)
				{
					throw std::invalid_argument("bins size not the same");
				}

				for (size_t i = 0; i < mBinsSize; i++)
				{
					m_bins[i] = bins[i];
				}

				return *this;
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Set(const Histogram_<Precision, Types...>& histogram)
			{
				return Set(histogram.Bins());
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Add(const Precision value)
			{
				return Apply([value](Precision bin){return bin+value;});
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Add(const std::vector<Precision>& bins)
			{
				size_t mBinsSize = m_bins.size();
				size_t binsSize = bins.size();

				if (mBinsSize != binsSize)
				{
					throw std::invalid_argument("bins size not the same");
				}

				for (size_t i = 0; i < mBinsSize; i++)
				{
					m_bins[i] += bins[i];
				}

				return *this;
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Add(const Histogram_<Precision, Types...>& histogram)
			{
				return Add(histogram.Bins());
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Clear()
			{
				return Set(Precision(0));
			}

			//-----------------------------------------------

			const std::vector<Precision>&
			Bins() const
			{
				return m_bins;
			}

			//-----------------------------------------------

			const std::function<uint32_t(Types...)>
			IndexFunction() const
			{
				return m_indexOf;
			}

			//-----------------------------------------------

			Precision
			Value(Types... types) const
			{
				auto index = m_indexOf(types...);
				return m_bins.at(index);
			}

			//-----------------------------------------------

			/*
			 * Sum of all bins values
			 */
			Precision
			Sum() const
			{
				Precision sum = 0;

				for (const auto& bin : m_bins)
				{
					sum += std::abs(bin);
				}

				return sum;
			}

			//-----------------------------------------------

			/*
			 * Normalize bins so values sum up to 1.0
			 */
			Histogram_<Precision, Types...>&
			Normalize()
			{
				Precision sum = Sum();

				if (sum > Precision(0))
				{
					for (auto& bin : m_bins)
					{
						bin /= sum;
					}
				}

				return *this;
			}

			//-----------------------------------------------
			static Histogram_<Precision, Types...>
			Add(std::initializer_list<Histogram_<Precision, Types...>> histograms)
			{
				auto size = histograms.size();

				if (size <= 0)
				{
					throw std::invalid_argument("cannot add zero histograms");
				}

				Histogram_<Precision, Types...> result(histograms[0]);

				for (size_t i = 1; i < size; i++)
				{
					result.Add(histograms[i]);
				}

				return result;
			}
	};

	//=========================================================================

	template<
		typename Precision,
		typename... Types>
	class HistogramUniformND_ :
			public Histogram_<Precision, Types...>
	{
		public:
			HistogramUniformND_(
					MinMaxBins<Types>... mS) :
						Histogram_<Precision, Types...>(
								NumberOfBins(mS...),
								Factory(mS...))
			{}
	};

  //=========================================================================
}

#endif
<commit_msg>Update Histogram_ND.hpp<commit_after>
#pragma once
#ifndef HISTOGRAM_ND_HPP
#define HISTOGRAM_ND_HPP

/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cmath>
#include <vector>
#include <functional>
#include <initializer_list>

//=========================================================================
// Example 0
//
// 2-d histogram with double precision, one dimension is int, other is float
// dimension 0 : takes integers with min 0, max 10 and 10 buckets
// dimension 1 : takes floats with min 0, max 10 and 15 buckets
//
// auto histogram2d =
//		Histogram::HistogramUniformND_<double, int, float>(
//			MinMaxBins<int>{0,10,10},
//			MinMaxBins<float>{0,10,15});
// histogram2d.Inc(1, 2.0f);
// histogram2d.Inc(3, 4.0f);
// histogram2d.Inc(5, 5.0f);
// histogram2d.Inc(15, 25.0f);
// histogram2d.Inc(25, 35.0f);
// histogram2d.Inc(25, 35.0f);
//
// auto value = histogram2d.Value(5, 5.0f);
//=========================================================================
namespace Histogram
{
	template<typename T>
	struct MinMaxBins
	{
		T min;
		T max;
		uint32_t bins;
	};

	template<typename Type>
	std::function<uint32_t(Type)>
	FactoryUniformIndexFunction1D_(
			const MinMaxBins<Type>& m)
	{
		if (m.min >= m.max)
		{
			throw std::invalid_argument("min should be < max");
		}

		if (m.bins <= 0)
		{
			throw std::invalid_argument("bins must be > 0");
		}

		return
			[m](Type value)
			{
				if (value <= m.min)
				{
					return uint32_t(0);
				}

				if (value >= m.max)
				{
					return uint32_t(m.bins - 1);
				}

				return uint32_t(std::round(
						double(value) * double(m.bins - 1) / double(m.max - m.min)));
			};
	}

	//=========================================================================

	template<typename T>
	inline uint32_t
	NumberOfBins(
			MinMaxBins<T> minMaxBin)
	{
		return minMaxBin.bins;
	}

	template<typename T, typename... Args>
	inline uint32_t
	NumberOfBins(
			MinMaxBins<T> minMaxBin,
			MinMaxBins<Args>... minMaxBins)
	{
		return minMaxBin.bins * NumberOfBins(minMaxBins...);
	}

	//=========================================================================

	template<typename T>
	inline std::function<uint32_t(T)>
	Factory(MinMaxBins<T> m0)
	{
		auto f0 = FactoryUniformIndexFunction1D_<T>(m0);
		return [f0](T t0){ return f0(t0);};
	}

	template<typename T, typename... Args>
	inline std::function<uint32_t(T, Args...)>
	Factory(MinMaxBins<T> m0, MinMaxBins<Args>... mN)
	{
		auto f0 = FactoryUniformIndexFunction1D_<T>(m0);

		return
			[f0, m0, mN...](T t0, Args... args)
			{
				return f0(t0) + m0.bins * Factory<Args...>(mN...)(args...);
			};
	}

	//=========================================================================

	template<
		typename Precision,
		typename... Types>
	class Histogram_
	{
		protected:
			std::vector<Precision> m_bins;
			std::function<uint32_t(Types...)> m_indexOf = nullptr;
		public:
			//-----------------------------------------------
			Histogram_(
					uint32_t noBins,
					std::function<uint32_t(Types...)> indexFunction)
			{
				if (noBins <= 0)
				{
					throw std::invalid_argument("bins should be > 0");
				}

				if (indexFunction == nullptr)
				{
					throw std::invalid_argument("index function should be > 0");
				}

				m_indexOf = indexFunction;
				m_bins = std::vector<Precision>(noBins, Precision(0));
			}

			//-----------------------------------------------

			Histogram_(
					const Histogram_& histogram) :
						Histogram_(histogram.Bins().size(), histogram.IndexFunction())
			{
				Set(histogram);
			}

			//-----------------------------------------------

			~Histogram_()
			{}

			//-----------------------------------------------

			/*
			 * Apply operation on bins
			 */
			Histogram_<Precision, Types...>&
			Apply(const std::function<Precision(Precision)> func)
			{
				std::for_each(
						m_bins.begin(),
						m_bins.end(),
						[func](Precision& bin)
						{
							bin = func(bin);
						});
				return *this;
			}

			//-----------------------------------------------

			Histogram_& IncMultiplier(Precision multiplier, Types... types)
			{
				auto index = m_indexOf(types...);
				m_bins[index] += multiplier;
				return *this;
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Inc(Types... types)
			{
				return IncMultiplier(Precision(1), types...);
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Set(Precision value)
			{
				return Apply([value](Precision bin){return value;});
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Set(const std::vector<Precision>& bins)
			{
				size_t mBinsSize = m_bins.size();
				size_t binsSize = bins.size();

				if (mBinsSize != binsSize)
				{
					throw std::invalid_argument("bins size not the same");
				}

				for (size_t i = 0; i < mBinsSize; i++)
				{
					m_bins[i] = bins[i];
				}

				return *this;
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Set(const Histogram_<Precision, Types...>& histogram)
			{
				return Set(histogram.Bins());
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Add(const Precision value)
			{
				return Apply([value](Precision bin){return bin+value;});
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Add(const std::vector<Precision>& bins)
			{
				size_t mBinsSize = m_bins.size();
				size_t binsSize = bins.size();

				if (mBinsSize != binsSize)
				{
					throw std::invalid_argument("bins size not the same");
				}

				for (size_t i = 0; i < mBinsSize; i++)
				{
					m_bins[i] += bins[i];
				}

				return *this;
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Add(const Histogram_<Precision, Types...>& histogram)
			{
				return Add(histogram.Bins());
			}

			//-----------------------------------------------

			Histogram_<Precision, Types...>&
			Clear()
			{
				return Set(Precision(0));
			}

			//-----------------------------------------------

			const std::vector<Precision>&
			Bins() const
			{
				return m_bins;
			}

			//-----------------------------------------------

			const std::function<uint32_t(Types...)>
			IndexFunction() const
			{
				return m_indexOf;
			}

			//-----------------------------------------------

			Precision
			Value(Types... types) const
			{
				auto index = m_indexOf(types...);
				return m_bins.at(index);
			}

			//-----------------------------------------------

			/*
			 * Sum of all bins values
			 */
			Precision
			Sum() const
			{
				Precision sum = 0;

				for (const auto& bin : m_bins)
				{
					sum += std::abs(bin);
				}

				return sum;
			}

			//-----------------------------------------------

			/*
			 * Normalize bins so values sum up to 1.0
			 */
			Histogram_<Precision, Types...>&
			Normalize()
			{
				Precision sum = Sum();

				if (sum > Precision(0))
				{
					for (auto& bin : m_bins)
					{
						bin /= sum;
					}
				}

				return *this;
			}

			//-----------------------------------------------
			static Histogram_<Precision, Types...>
			Add(std::initializer_list<Histogram_<Precision, Types...>> histograms)
			{
				auto size = histograms.size();

				if (size <= 0)
				{
					throw std::invalid_argument("cannot add zero histograms");
				}

				Histogram_<Precision, Types...> result(histograms[0]);

				for (size_t i = 1; i < size; i++)
				{
					result.Add(histograms[i]);
				}

				return result;
			}
	};

	//=========================================================================

	template<
		typename Precision,
		typename... Types>
	class HistogramUniformND_ :
			public Histogram_<Precision, Types...>
	{
		public:
			HistogramUniformND_(
					MinMaxBins<Types>... mS) :
						Histogram_<Precision, Types...>(
								NumberOfBins(mS...),
								Factory(mS...))
			{}
	};

  //=========================================================================
}

#endif
<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/grappler/optimizers/data/latency_all_edges.h"

#include "tensorflow/core/framework/attr_value.pb.h"
#include "tensorflow/core/framework/node_def.pb.h"
#include "tensorflow/core/grappler/clusters/cluster.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/mutable_graph_view.h"
#include "tensorflow/core/grappler/op_types.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h"
#include "tensorflow/core/grappler/optimizers/data/graph_utils.h"
#include "tensorflow/core/grappler/utils.h"
#include "tensorflow/core/kernels/data/stats_utils.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/protobuf.h"

namespace tensorflow {
namespace grappler {
namespace {

constexpr char kInsertOpName[] = "LatencyStatsDataset";

NodeDef MakeLatencyNode(const NodeDef& node, MutableGraphView* graph) {
  NodeDef new_node;
  new_node.set_op(kInsertOpName);
  graph_utils::SetUniqueGraphNodeName(strings::StrCat(kInsertOpName),
                                      graph->graph(), &new_node);
  // Set the input of LatencyDataset node as `node`
  new_node.add_input(node.name());

  string tag_name = strings::StrCat("record_latency",
                                    data::stats_utils::kDelimiter, node.name());
  NodeDef* tag = graph_utils::AddScalarConstNode<StringPiece>(
      StringPiece(tag_name), graph);
  new_node.add_input(tag->name());

  // Set `output_types` and `output_shapes` attributes.
  for (auto key : {"output_shapes", "output_types"}) {
    if (node.attr().find(key) != node.attr().end()) {
      (*new_node.mutable_attr())[key] = node.attr().at(key);
    } else {
      const char* kInferredAttrPrefix = "T";
      if (node.attr().find(strings::StrCat(kInferredAttrPrefix, key)) !=
          node.attr().end()) {
        (*new_node.mutable_attr())[key] =
            node.attr().at(strings::StrCat(kInferredAttrPrefix, key));
      }
    }
  }
  return new_node;
}

}  // namespace

Status LatencyAllEdges::OptimizeAndCollectStats(Cluster* cluster,
                                                const GrapplerItem& item,
                                                GraphDef* output,
                                                OptimizationStats* stats) {
  *output = item.graph;
  MutableGraphView graph(output);

  // Add LatencyDatasetOp node after each node.
  // TODO(shivaniagrawal): Add Op to return Latency for the particular Op than
  // for the edge (e2 - e1?).
  for (const NodeDef& node : item.graph.node()) {
    if (!absl::EndsWith(node.op(), "Dataset") || node.attr().empty()) {
      // TODO(b/111805951): Replace this with non-approximate way to check if
      // node corresponds to a `Dataset` op.
      continue;
    }
    NodeDef* latency_node = graph.AddNode(MakeLatencyNode(node, &graph));
    TF_RETURN_IF_ERROR(graph.UpdateFanouts(node.name(), latency_node->name()));
    stats->num_changes++;
  }
  return Status::OK();
}

void LatencyAllEdges::Feedback(Cluster* cluster, const GrapplerItem& item,
                               const GraphDef& optimize_output, double result) {
  // no-op
}

REGISTER_GRAPH_OPTIMIZER_AS(LatencyAllEdges, "latency_all_edges");

}  // namespace grappler
}  // namespace tensorflow
<commit_msg>[tf.data] In latency_all_edges, if any of the nodes don't have output_types or output_shapes attrs, don't add a latency stats node after it.<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/grappler/optimizers/data/latency_all_edges.h"

#include "tensorflow/core/framework/attr_value.pb.h"
#include "tensorflow/core/framework/node_def.pb.h"
#include "tensorflow/core/grappler/clusters/cluster.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/mutable_graph_view.h"
#include "tensorflow/core/grappler/op_types.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h"
#include "tensorflow/core/grappler/optimizers/data/graph_utils.h"
#include "tensorflow/core/grappler/utils.h"
#include "tensorflow/core/kernels/data/stats_utils.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/protobuf.h"

namespace tensorflow {
namespace grappler {
namespace {

constexpr char kInsertOpName[] = "LatencyStatsDataset";

// Creates a LatencyStatsDataset node whose input is `node`.
Status MakeLatencyNode(const NodeDef& node, MutableGraphView* graph,
                       NodeDef* result) {
  result->set_op(kInsertOpName);
  graph_utils::SetUniqueGraphNodeName(strings::StrCat(kInsertOpName),
                                      graph->graph(), result);
  // Set the input of LatencyDataset node as `node`
  result->add_input(node.name());

  string tag_name = strings::StrCat("record_latency",
                                    data::stats_utils::kDelimiter, node.name());
  NodeDef* tag = graph_utils::AddScalarConstNode<StringPiece>(
      StringPiece(tag_name), graph);
  result->add_input(tag->name());

  // Set `output_types` and `output_shapes` attributes by copying the relevant
  // attrs from the input node. This is an imperfect heuristic; some dataset ops
  // might not have these attrs. If we encounter such an op, return an error
  // instead of creating a node.
  for (auto key : {"output_shapes", "output_types"}) {
    if (node.attr().find(key) != node.attr().end()) {
      (*result->mutable_attr())[key] = node.attr().at(key);
    } else {
      const char* kInferredAttrPrefix = "T";
      if (node.attr().find(strings::StrCat(kInferredAttrPrefix, key)) !=
          node.attr().end()) {
        (*result->mutable_attr())[key] =
            node.attr().at(strings::StrCat(kInferredAttrPrefix, key));
      } else {
        return errors::InvalidArgument(
            "Could not create LatencyStatsDataset after ", node.op(),
            " node because it does not have a (T)output_types or output_shapes "
            "attr.");
      }
    }
  }
  return Status::OK();
}

}  // namespace

Status LatencyAllEdges::OptimizeAndCollectStats(Cluster* cluster,
                                                const GrapplerItem& item,
                                                GraphDef* output,
                                                OptimizationStats* stats) {
  *output = item.graph;
  MutableGraphView graph(output);

  // Add LatencyDatasetOp node after each node.
  // TODO(shivaniagrawal): Add Op to return Latency for the particular Op than
  // for the edge (e2 - e1?).
  for (const NodeDef& node : item.graph.node()) {
    if (!absl::EndsWith(node.op(), "Dataset") || node.attr().empty()) {
      // TODO(b/111805951): Replace this with non-approximate way to check if
      // node corresponds to a `Dataset` op.
      continue;
    }
    NodeDef latency_node;
    // Try to make a latency node. This may fail if the input node doesn't have
    // output_types or output_shapes attrs. In those cases, we don't add a node
    // after `node`.
    Status s = MakeLatencyNode(node, &graph, &latency_node);
    if (s.ok()) {
      NodeDef* latency_node_pointer = graph.AddNode(std::move(latency_node));
      TF_RETURN_IF_ERROR(
          graph.UpdateFanouts(node.name(), latency_node_pointer->name()));
      stats->num_changes++;
    } else {
      LOG(WARNING) << s.error_message();
    }
  }
  return Status::OK();
}

void LatencyAllEdges::Feedback(Cluster* cluster, const GrapplerItem& item,
                               const GraphDef& optimize_output, double result) {
  // no-op
}

REGISTER_GRAPH_OPTIMIZER_AS(LatencyAllEdges, "latency_all_edges");

}  // namespace grappler
}  // namespace tensorflow
<|endoftext|>