<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    vtkWSLookupTableManager.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "vtkWSLookupTableManager.h"
#include "vtkObjectFactory.h"
#include <fstream>

vtkWSLookupTableManager* vtkWSLookupTableManager::New()
{
  // First try to create the object from the vtkObjectFactory
  vtkObject* ret = vtkObjectFactory::CreateInstance("vtkWSLookupTableManager");
  if(ret)
    {
    return (vtkWSLookupTableManager*)ret;
    }
  // If the factory was unable to create the object, then create it here.
  return new vtkWSLookupTableManager;
}

vtkWSLookupTableManager::vtkWSLookupTableManager()
{
  // Initialize member variables.
  this->LookupTable = 0;
  //  this->LookupTable = vtkPatchedLookupTable::New();
  this->CurrentThreshold       = 0.0;
  this->CurrentPositionPointer = 0;
  this->MergeList              = 0;
  this->NumberOfLabels         = 0;
  this->MaximumSaliency        = 0.0;
  this->ComputedEquivalencyList= 0;
  this->HighlightColor[0] = 1.0;
  this->HighlightColor[1] = 1.0;
  this->HighlightColor[2] = 1.0;
  this->RepaintHighlights = 0;
}

vtkWSLookupTableManager::~vtkWSLookupTableManager()
{
  if (this->MergeList != 0) { delete[] this->MergeList; }
  if (this->ComputedEquivalencyList !=0 )
    { delete[] this->ComputedEquivalencyList; }
  this->LookupTable->Delete();
}

void vtkWSLookupTableManager::CompileEquivalenciesFor(int x, int y, int z,
                                                      vtkImageData *data)
{
  // Find the value at x,y,z
  unsigned long n = * ( (unsigned long *) (data->GetScalarPointer(x,y,z)) );
  this->CompileEquivalenciesFor(n);
}

void vtkWSLookupTableManager::AppendEquivalenciesFor(int x, int y, int z,
                                                      vtkImageData *data)
{
  // Find the value at x,y,z
  unsigned long n = * ( (unsigned long *) (data->GetScalarPointer(x,y,z)) );
  this->AppendEquivalenciesFor(n);
}

void vtkWSLookupTableManager::RecompileEquivalencies()
{
  if (this->ComputedEquivalencyList ==0 ) return;

  // Get a region label in the current list and recompute the list
  // based on that value.
 this->CompileEquivalenciesFor(ComputedEquivalencyList[1]);
}

void vtkWSLookupTableManager::AppendEquivalenciesFor(unsigned long n)
{
  unsigned_long_list_t list;

  // If there is already an equivalency list,
  //   then initialize the new list with those values.
  if (this->ComputedEquivalencyList != 0)
    { // ignore the first entry, this was the old size of the list
      for (unsigned int i = 1; i < ComputedEquivalencyList[0] +1; ++i)
        {
          list.push_back(ComputedEquivalencyList[i]);
        }
    }

  if (this->ComputedEquivalencyList !=0 )
    { delete[] this->ComputedEquivalencyList; }

  // Get the real value we are looking for.
  unsigned long real_n = EquivalencyTable.RecursiveLookup(n);
  list.push_back(real_n);

  // Search for equivalencies.
  for (vtkLookupTableEquivalencyHash::ConstIterator hashIt = EquivalencyTable.Begin();
       hashIt != EquivalencyTable.End(); hashIt++)
    {
      if (EquivalencyTable.RecursiveLookup( (*hashIt).first ) == real_n)
        list.push_back( (*hashIt).first );
    }

  // Copy the list into the array
  this->ComputedEquivalencyList = new unsigned long[list.size() + 1];
  ComputedEquivalencyList[0] = (unsigned long) list.size();
  unsigned long i = 1;
  for (unsigned_long_list_t::iterator it = list.begin();
       it != list.end(); it++, i++)
    {
      ComputedEquivalencyList[i] = *it;
    }

}

void vtkWSLookupTableManager::CompileEquivalenciesFor(unsigned long n)
{
  if (this->ComputedEquivalencyList !=0 )
    { delete[] this->ComputedEquivalencyList; }

  unsigned_long_list_t list;

  // Get the real value we are looking for.
  unsigned long real_n = EquivalencyTable.RecursiveLookup(n);
  list.push_back(real_n);

  // Search for equivalencies.
  for (vtkLookupTableEquivalencyHash::ConstIterator hashIt = EquivalencyTable.Begin();
       hashIt != EquivalencyTable.End(); hashIt++)
    {
      if (EquivalencyTable.RecursiveLookup( (*hashIt).first ) == real_n)
        list.push_back( (*hashIt).first );
    }

  // Copy the list into the array
  this->ComputedEquivalencyList = new unsigned long[list.size() + 1];
  ComputedEquivalencyList[0] = (unsigned long) list.size();
  unsigned long i = 1;
  for (unsigned_long_list_t::iterator it = list.begin();
       it != list.end(); it++, i++)
    {
      ComputedEquivalencyList[i] = *it;
    }
}

void vtkWSLookupTableManager::HighlightValue( unsigned long n )
{
  // Add to highlight list
  this->HighlightedValueList.push_back(n);

  // Set to selected color
  this->LookupTable->SetTableValue(n,
                                   HighlightColor[0],
                                   HighlightColor[1],
                                   HighlightColor[2]);
}

void vtkWSLookupTableManager::UnHighlightValue(unsigned long n)
{
  this->HighlightedValueList.remove(n);
  this->LookupTable->SetTableValue(n,
                                   (float)(rand() / (RAND_MAX + 1.0)),
                                   (float)(rand() / (RAND_MAX + 1.0)),
                                   (float)(rand() / (RAND_MAX + 1.0)) );
}

void vtkWSLookupTableManager::RepaintHighlightedValues()
{
    unsigned_long_list_t::iterator it;
  for (it = HighlightedValueList.begin();
       it != HighlightedValueList.end();
       it++)
    {
      this->LookupTable->SetTableValue(*it,
                                       HighlightColor[0],
                                       HighlightColor[1],
                                       HighlightColor[2]);
    }

}

void vtkWSLookupTableManager::ClearHighlightedValues()
{
  unsigned_long_list_t::iterator it;
  for (it = HighlightedValueList.begin();
       it != HighlightedValueList.end();
       it++)
    {
      this->LookupTable->SetTableValue(*it,
                                       (float)(rand() / (RAND_MAX + 1.0)),
                                       (float)(rand() / (RAND_MAX + 1.0)),
                                       (float)(rand() / (RAND_MAX + 1.0)) );
    }
  HighlightedValueList.clear();
}

void vtkWSLookupTableManager::ClearHighlightedValuesToSameColor()
{
  float r =  (float)(rand() / (RAND_MAX + 1.0));
  float g =  (float)(rand() / (RAND_MAX + 1.0));
  float b =  (float)(rand() / (RAND_MAX + 1.0));

  unsigned_long_list_t::iterator it;
  for (it = HighlightedValueList.begin();
       it != HighlightedValueList.end();
       it++)
    {
      this->LookupTable->SetTableValue(*it, r, g, b);
    }
  HighlightedValueList.clear();
}

void vtkWSLookupTableManager::HighlightComputedEquivalencyList()
{
  if (this->ComputedEquivalencyList == 0)  { return;  }

  unsigned long i;
  for (i = 1; i <= ComputedEquivalencyList[0]; i++ )
    {
      this->HighlightValue( ComputedEquivalencyList[i] );
    }

}

void vtkWSLookupTableManager::GenerateColorTable()
{
  for (unsigned long i = 0; i < this->LookupTable->GetNumberOfTableValues(); i++)
    {
      this->LookupTable->SetTableValue(i, (float)(rand() / (RAND_MAX + 1.0)),
                                          (float)(rand() / (RAND_MAX + 1.0)),
                                          (float)(rand() / (RAND_MAX + 1.0)) );
    }
  if (this->RepaintHighlights !=0 ) this->RepaintHighlightedValues();
}

void vtkWSLookupTableManager::Initialize()
{
  // Delete old and allocate new table.
  if ( this->LookupTable !=0 )
    {
      this->LookupTable->Delete();
    }
  this->LookupTable = vtkPatchedLookupTable::New();

  // Reset all other values and tables
  if (this->MergeList != 0) delete[] this->MergeList;
  if (this->ComputedEquivalencyList !=0 ) delete[] this->ComputedEquivalencyList;
  this->CurrentPositionPointer = 0;
  HighlightedValueList.clear();
  EquivalencyTable.Clear();

  this->CurrentThreshold       = 0.0;
  this->CurrentPositionPointer = 0;
  this->MergeList              = 0;
  this->NumberOfLabels         = 0;
  this->MaximumSaliency        = 0.0;
  this->ComputedEquivalencyList= 0;
}

void vtkWSLookupTableManager::LoadTreeFile(const char* fn)
{
  ifstream in(fn);
  if (!in)
    {
      vtkErrorMacro (<<"Bad file name: " << fn);
      exit(-1);
    }

  // read header
  unsigned long listsz;
  in.read((char *)&listsz, sizeof(unsigned long));

  // allocate merge list
  if (this->MergeList != 0) delete[] this->MergeList;
  this->MergeList = new merge_t[listsz + 2];

  // now read the data
  in.read((char *)(this->MergeList + 1), listsz * sizeof(merge_t));

  if (in.gcount() != listsz *sizeof(merge_t))
    {
      vtkErrorMacro(<<"Error reading " << fn << ". File size does not match header size.");
      exit(-1);
    }

  in.close();

  // set current position to beginning of list & reset threshold
  this->CurrentPositionPointer = this->MergeList + 1;
  this->CurrentThreshold       = 0.0;

  // set the maximum saliency
  this->MaximumSaliency = (this->MergeList + listsz)->saliency;

  // mark the first and last (empty) elements
  (this->MergeList + listsz + 1)->saliency = -2.0; // end
  this->MergeList->saliency = -1.0; // start

}

void vtkWSLookupTableManager::SetNumberOfLabels(unsigned long n)
{
  this->NumberOfLabels = n;
  this->LookupTable->SetTableRange(0, n);
  this->LookupTable->SetNumberOfColors(n);
  this->LookupTable->SetNumberOfTableValues(n);
  this->Modified();
}

float vtkWSLookupTableManager::UndoLastMerge()
{
  if (UndoPositionPointer == 0 ) return 0.0;

  float threshold = (float) ( (double) (UndoPositionPointer->saliency) /
    (double) MaximumSaliency );

  this->Merge(threshold);

  return CurrentThreshold;

}

float vtkWSLookupTableManager::MergeSelected()
{
  if ( ComputedEquivalencyList == 0 ) return CurrentThreshold;

  // Find the current leaf node represented by the ComputedEquivalencyList.
  // Just need to look at the first element in the list (guaranteed to be
  // there).
  unsigned long leaf_node =
    EquivalencyTable.RecursiveLookup(ComputedEquivalencyList[1]);
  return (this->Merge(leaf_node));
}

float vtkWSLookupTableManager::Merge(unsigned long n)
{
  // Are we at the end of the list?
  if ( (CurrentPositionPointer + 1)->saliency < 0 ) return 1.0;

  UndoPositionPointer = CurrentPositionPointer;

  // Find the threshold of the next merge of this leaf node n or the threshold
  // of the next merge of any node with this one.
  if (MergeList == 0 || CurrentPositionPointer == 0 || MaximumSaliency == 0.0)
    {
      vtkErrorMacro("No segment tree has been specified for merging.");
      exit(-1);
    }

  while (CurrentPositionPointer->from != n
         && CurrentPositionPointer->to !=n )
    {
      EquivalencyTable.Add(CurrentPositionPointer->from,
                           CurrentPositionPointer->to);

      CurrentPositionPointer++;

      // stop at the end of the list
      if (CurrentPositionPointer->saliency < 0)
        {
          CurrentPositionPointer--;
          break;
        }
    }

  // Do the next merge, which is the node we were looking for.
  EquivalencyTable.Add(CurrentPositionPointer->from,
                       CurrentPositionPointer->to);

  CurrentPositionPointer++;

  // stop at the end of the list
  if (CurrentPositionPointer->saliency < 0)
    {
      CurrentPositionPointer--;
    }

  // Merge the equivalent segments' colors in the lookup table.
  this->MergeEquivalencies();
  CurrentThreshold = (float) ( (double)(CurrentPositionPointer->saliency) /
                               (double)MaximumSaliency );

  return CurrentThreshold;
}

void vtkWSLookupTableManager::Merge(float t)
{
  if (this->LookupTable == 0 ) return;

  float v;

  v = t * MaximumSaliency;

  // save current state for undo
  UndoPositionPointer = CurrentPositionPointer;

  if ( CurrentThreshold <= t)  // Move up the tree
    {
      while ( CurrentPositionPointer->saliency <= v )
        {
          EquivalencyTable.Add(CurrentPositionPointer->from,
                               CurrentPositionPointer->to);

          CurrentPositionPointer++;

          // stop at the end of the list
          if (CurrentPositionPointer->saliency < 0)
            {
              CurrentPositionPointer--;
              break;
            }
        }

    }

  else if ( CurrentThreshold > t)  // Move down the tree
    {
      while ( CurrentPositionPointer->saliency > v )
        {
          EquivalencyTable.Erase(CurrentPositionPointer->from);
          LookupTable->SetTableValue(CurrentPositionPointer->from,
                                     (float)(rand() / (RAND_MAX + 1.0)),
                                     (float)(rand() / (RAND_MAX + 1.0)),
                                     (float)(rand() / (RAND_MAX + 1.0)) );
          CurrentPositionPointer--;

          // stop at the beginning of the list
          if (CurrentPositionPointer->saliency < 0)
            {
              CurrentPositionPointer++;
              break;
            }

        }

    }

  // Merge the equivalent segments' colors in the lookup table.
  this->MergeEquivalencies();
  CurrentThreshold = t;
}

void vtkWSLookupTableManager::MergeEquivalencies()
{

  // Merge colors in the lookup table
  vtkLookupTableEquivalencyHash::Iterator it;
  it = EquivalencyTable.Begin();

  while (it != EquivalencyTable.End())
    {
      LookupTable->SetTableValue((*it).first,
        LookupTable->GetTableValue(EquivalencyTable.RecursiveLookup((*it).second)));
      it++;
    }

}

<commit_msg>FIX: Specified binary versus default file input format.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    vtkWSLookupTableManager.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "vtkWSLookupTableManager.h"
#include "vtkObjectFactory.h"
#include <fstream>

vtkWSLookupTableManager* vtkWSLookupTableManager::New()
{
  // First try to create the object from the vtkObjectFactory
  vtkObject* ret = vtkObjectFactory::CreateInstance("vtkWSLookupTableManager");
  if(ret)
    {
    return (vtkWSLookupTableManager*)ret;
    }
  // If the factory was unable to create the object, then create it here.
  return new vtkWSLookupTableManager;
}

vtkWSLookupTableManager::vtkWSLookupTableManager()
{
  // Initialize member variables.
  this->LookupTable = 0;
  //  this->LookupTable = vtkPatchedLookupTable::New();
  this->CurrentThreshold       = 0.0;
  this->CurrentPositionPointer = 0;
  this->MergeList              = 0;
  this->NumberOfLabels         = 0;
  this->MaximumSaliency        = 0.0;
  this->ComputedEquivalencyList= 0;
  this->HighlightColor[0] = 1.0;
  this->HighlightColor[1] = 1.0;
  this->HighlightColor[2] = 1.0;
  this->RepaintHighlights = 0;
}

vtkWSLookupTableManager::~vtkWSLookupTableManager()
{
  if (this->MergeList != 0) { delete[] this->MergeList; }
  if (this->ComputedEquivalencyList !=0 )
    { delete[] this->ComputedEquivalencyList; }
  this->LookupTable->Delete();
}

void vtkWSLookupTableManager::CompileEquivalenciesFor(int x, int y, int z,
                                                      vtkImageData *data)
{
  // Find the value at x,y,z
  unsigned long n = * ( (unsigned long *) (data->GetScalarPointer(x,y,z)) );
  this->CompileEquivalenciesFor(n);
}

void vtkWSLookupTableManager::AppendEquivalenciesFor(int x, int y, int z,
                                                      vtkImageData *data)
{
  // Find the value at x,y,z
  unsigned long n = * ( (unsigned long *) (data->GetScalarPointer(x,y,z)) );
  this->AppendEquivalenciesFor(n);
}

void vtkWSLookupTableManager::RecompileEquivalencies()
{
  if (this->ComputedEquivalencyList ==0 ) return;

  // Get a region label in the current list and recompute the list
  // based on that value.
 this->CompileEquivalenciesFor(ComputedEquivalencyList[1]);
}

void vtkWSLookupTableManager::AppendEquivalenciesFor(unsigned long n)
{
  unsigned_long_list_t list;

  // If there is already an equivalency list,
  //   then initialize the new list with those values.
  if (this->ComputedEquivalencyList != 0)
    { // ignore the first entry, this was the old size of the list
      for (unsigned int i = 1; i < ComputedEquivalencyList[0] +1; ++i)
        {
          list.push_back(ComputedEquivalencyList[i]);
        }
    }

  if (this->ComputedEquivalencyList !=0 )
    { delete[] this->ComputedEquivalencyList; }

  // Get the real value we are looking for.
  unsigned long real_n = EquivalencyTable.RecursiveLookup(n);
  list.push_back(real_n);

  // Search for equivalencies.
  for (vtkLookupTableEquivalencyHash::ConstIterator hashIt = EquivalencyTable.Begin();
       hashIt != EquivalencyTable.End(); hashIt++)
    {
      if (EquivalencyTable.RecursiveLookup( (*hashIt).first ) == real_n)
        list.push_back( (*hashIt).first );
    }

  // Copy the list into the array
  this->ComputedEquivalencyList = new unsigned long[list.size() + 1];
  ComputedEquivalencyList[0] = (unsigned long) list.size();
  unsigned long i = 1;
  for (unsigned_long_list_t::iterator it = list.begin();
       it != list.end(); it++, i++)
    {
      ComputedEquivalencyList[i] = *it;
    }

}

void vtkWSLookupTableManager::CompileEquivalenciesFor(unsigned long n)
{
  if (this->ComputedEquivalencyList !=0 )
    { delete[] this->ComputedEquivalencyList; }

  unsigned_long_list_t list;

  // Get the real value we are looking for.
  unsigned long real_n = EquivalencyTable.RecursiveLookup(n);
  list.push_back(real_n);

  // Search for equivalencies.
  for (vtkLookupTableEquivalencyHash::ConstIterator hashIt = EquivalencyTable.Begin();
       hashIt != EquivalencyTable.End(); hashIt++)
    {
      if (EquivalencyTable.RecursiveLookup( (*hashIt).first ) == real_n)
        list.push_back( (*hashIt).first );
    }

  // Copy the list into the array
  this->ComputedEquivalencyList = new unsigned long[list.size() + 1];
  ComputedEquivalencyList[0] = (unsigned long) list.size();
  unsigned long i = 1;
  for (unsigned_long_list_t::iterator it = list.begin();
       it != list.end(); it++, i++)
    {
      ComputedEquivalencyList[i] = *it;
    }
}

void vtkWSLookupTableManager::HighlightValue( unsigned long n )
{
  // Add to highlight list
  this->HighlightedValueList.push_back(n);

  // Set to selected color
  this->LookupTable->SetTableValue(n,
                                   HighlightColor[0],
                                   HighlightColor[1],
                                   HighlightColor[2]);
}

void vtkWSLookupTableManager::UnHighlightValue(unsigned long n)
{
  this->HighlightedValueList.remove(n);
  this->LookupTable->SetTableValue(n,
                                   (float)(rand() / (RAND_MAX + 1.0)),
                                   (float)(rand() / (RAND_MAX + 1.0)),
                                   (float)(rand() / (RAND_MAX + 1.0)) );
}

void vtkWSLookupTableManager::RepaintHighlightedValues()
{
    unsigned_long_list_t::iterator it;
  for (it = HighlightedValueList.begin();
       it != HighlightedValueList.end();
       it++)
    {
      this->LookupTable->SetTableValue(*it,
                                       HighlightColor[0],
                                       HighlightColor[1],
                                       HighlightColor[2]);
    }

}

void vtkWSLookupTableManager::ClearHighlightedValues()
{
  unsigned_long_list_t::iterator it;
  for (it = HighlightedValueList.begin();
       it != HighlightedValueList.end();
       it++)
    {
      this->LookupTable->SetTableValue(*it,
                                       (float)(rand() / (RAND_MAX + 1.0)),
                                       (float)(rand() / (RAND_MAX + 1.0)),
                                       (float)(rand() / (RAND_MAX + 1.0)) );
    }
  HighlightedValueList.clear();
}

void vtkWSLookupTableManager::ClearHighlightedValuesToSameColor()
{
  float r =  (float)(rand() / (RAND_MAX + 1.0));
  float g =  (float)(rand() / (RAND_MAX + 1.0));
  float b =  (float)(rand() / (RAND_MAX + 1.0));

  unsigned_long_list_t::iterator it;
  for (it = HighlightedValueList.begin();
       it != HighlightedValueList.end();
       it++)
    {
      this->LookupTable->SetTableValue(*it, r, g, b);
    }
  HighlightedValueList.clear();
}

void vtkWSLookupTableManager::HighlightComputedEquivalencyList()
{
  if (this->ComputedEquivalencyList == 0)  { return;  }

  unsigned long i;
  for (i = 1; i <= ComputedEquivalencyList[0]; i++ )
    {
      this->HighlightValue( ComputedEquivalencyList[i] );
    }

}

void vtkWSLookupTableManager::GenerateColorTable()
{
  for (unsigned long i = 0; i < this->LookupTable->GetNumberOfTableValues(); i++)
    {
      this->LookupTable->SetTableValue(i, (float)(rand() / (RAND_MAX + 1.0)),
                                          (float)(rand() / (RAND_MAX + 1.0)),
                                          (float)(rand() / (RAND_MAX + 1.0)) );
    }
  if (this->RepaintHighlights !=0 ) this->RepaintHighlightedValues();
}

void vtkWSLookupTableManager::Initialize()
{
  // Delete old and allocate new table.
  if ( this->LookupTable !=0 )
    {
      this->LookupTable->Delete();
    }
  this->LookupTable = vtkPatchedLookupTable::New();

  // Reset all other values and tables
  if (this->MergeList != 0) delete[] this->MergeList;
  if (this->ComputedEquivalencyList !=0 ) delete[] this->ComputedEquivalencyList;
  this->CurrentPositionPointer = 0;
  HighlightedValueList.clear();
  EquivalencyTable.Clear();

  this->CurrentThreshold       = 0.0;
  this->CurrentPositionPointer = 0;
  this->MergeList              = 0;
  this->NumberOfLabels         = 0;
  this->MaximumSaliency        = 0.0;
  this->ComputedEquivalencyList= 0;
}

void vtkWSLookupTableManager::LoadTreeFile(const char* fn)
{
  ifstream in(fn, ios::binary);
  if (!in)
    {
      vtkErrorMacro (<<"Bad file name: " << fn);
      exit(-1);
    }

  // read header
  unsigned long listsz;
  in.read((char *)&listsz, sizeof(unsigned long));

  // allocate merge list
  if (this->MergeList != 0) delete[] this->MergeList;
  this->MergeList = new merge_t[listsz + 2];

  // now read the data
  in.read((char *)(this->MergeList + 1), listsz * sizeof(merge_t));

  if (in.gcount() != listsz *sizeof(merge_t))
    {
      vtkErrorMacro(<<"Error reading " << fn << ". File size does not match header size.");
      exit(-1);
    }

  in.close();

  // set current position to beginning of list & reset threshold
  this->CurrentPositionPointer = this->MergeList + 1;
  this->CurrentThreshold       = 0.0;

  // set the maximum saliency
  this->MaximumSaliency = (this->MergeList + listsz)->saliency;

  // mark the first and last (empty) elements
  (this->MergeList + listsz + 1)->saliency = -2.0; // end
  this->MergeList->saliency = -1.0; // start

}

void vtkWSLookupTableManager::SetNumberOfLabels(unsigned long n)
{
  this->NumberOfLabels = n;
  this->LookupTable->SetTableRange(0, n);
  this->LookupTable->SetNumberOfColors(n);
  this->LookupTable->SetNumberOfTableValues(n);
  this->Modified();
}

float vtkWSLookupTableManager::UndoLastMerge()
{
  if (UndoPositionPointer == 0 ) return 0.0;

  float threshold = (float) ( (double) (UndoPositionPointer->saliency) /
    (double) MaximumSaliency );

  this->Merge(threshold);

  return CurrentThreshold;

}

float vtkWSLookupTableManager::MergeSelected()
{
  if ( ComputedEquivalencyList == 0 ) return CurrentThreshold;

  // Find the current leaf node represented by the ComputedEquivalencyList.
  // Just need to look at the first element in the list (guaranteed to be
  // there).
  unsigned long leaf_node =
    EquivalencyTable.RecursiveLookup(ComputedEquivalencyList[1]);
  return (this->Merge(leaf_node));
}

float vtkWSLookupTableManager::Merge(unsigned long n)
{
  // Are we at the end of the list?
  if ( (CurrentPositionPointer + 1)->saliency < 0 ) return 1.0;

  UndoPositionPointer = CurrentPositionPointer;

  // Find the threshold of the next merge of this leaf node n or the threshold
  // of the next merge of any node with this one.
  if (MergeList == 0 || CurrentPositionPointer == 0 || MaximumSaliency == 0.0)
    {
      vtkErrorMacro("No segment tree has been specified for merging.");
      exit(-1);
    }

  while (CurrentPositionPointer->from != n
         && CurrentPositionPointer->to !=n )
    {
      EquivalencyTable.Add(CurrentPositionPointer->from,
                           CurrentPositionPointer->to);

      CurrentPositionPointer++;

      // stop at the end of the list
      if (CurrentPositionPointer->saliency < 0)
        {
          CurrentPositionPointer--;
          break;
        }
    }

  // Do the next merge, which is the node we were looking for.
  EquivalencyTable.Add(CurrentPositionPointer->from,
                       CurrentPositionPointer->to);

  CurrentPositionPointer++;

  // stop at the end of the list
  if (CurrentPositionPointer->saliency < 0)
    {
      CurrentPositionPointer--;
    }

  // Merge the equivalent segments' colors in the lookup table.
  this->MergeEquivalencies();
  CurrentThreshold = (float) ( (double)(CurrentPositionPointer->saliency) /
                               (double)MaximumSaliency );

  return CurrentThreshold;
}

void vtkWSLookupTableManager::Merge(float t)
{
  if (this->LookupTable == 0 ) return;

  float v;

  v = t * MaximumSaliency;

  // save current state for undo
  UndoPositionPointer = CurrentPositionPointer;

  if ( CurrentThreshold <= t)  // Move up the tree
    {
      while ( CurrentPositionPointer->saliency <= v )
        {
          EquivalencyTable.Add(CurrentPositionPointer->from,
                               CurrentPositionPointer->to);

          CurrentPositionPointer++;

          // stop at the end of the list
          if (CurrentPositionPointer->saliency < 0)
            {
              CurrentPositionPointer--;
              break;
            }
        }

    }

  else if ( CurrentThreshold > t)  // Move down the tree
    {
      while ( CurrentPositionPointer->saliency > v )
        {
          EquivalencyTable.Erase(CurrentPositionPointer->from);
          LookupTable->SetTableValue(CurrentPositionPointer->from,
                                     (float)(rand() / (RAND_MAX + 1.0)),
                                     (float)(rand() / (RAND_MAX + 1.0)),
                                     (float)(rand() / (RAND_MAX + 1.0)) );
          CurrentPositionPointer--;

          // stop at the beginning of the list
          if (CurrentPositionPointer->saliency < 0)
            {
              CurrentPositionPointer++;
              break;
            }

        }

    }

  // Merge the equivalent segments' colors in the lookup table.
  this->MergeEquivalencies();
  CurrentThreshold = t;
}

void vtkWSLookupTableManager::MergeEquivalencies()
{

  // Merge colors in the lookup table
  vtkLookupTableEquivalencyHash::Iterator it;
  it = EquivalencyTable.Begin();

  while (it != EquivalencyTable.End())
    {
      LookupTable->SetTableValue((*it).first,
        LookupTable->GetTableValue(EquivalencyTable.RecursiveLookup((*it).second)));
      it++;
    }

}

<|endoftext|>


<commit_before>#ifndef __COMPUTE_TASK_HPP_INCLUDED
#define __COMPUTE_TASK_HPP_INCLUDED

#include "entity.hpp"
#include "shader.hpp"
#include "compute_task_struct.hpp"
#include "pre_iterate_callback.hpp"
#include "post_iterate_callback.hpp"
#include "render_templates.hpp"
#include "family_templates.hpp"
#include "code/ylikuutio/load/texture_loader.hpp"
#include "code/ylikuutio/opengl/opengl.hpp"
#include "code/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include GLEW
#include "code/ylikuutio/opengl/ylikuutio_glew.hpp" // GLfloat, GLuint etc.

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cstddef>  // std::size_t
#include <iostream> // std::cout, std::cin, std::cerr
#include <memory>   // std::make_shared, std::shared_ptr
#include <queue>    // std::queue
#include <stdint.h> // uint32_t etc.
#include <vector>   // std::vector

// `yli::ontology::ComputeTask` is a class which contains the data for a single
// computing task. `ComputeTask` does not have the OpenGL shaders used to process
// the data. Instead, the shaders are contained by the parent `Entity` which is
// an `yli::ontology::ComputeTask` instance.
//
// For example, `yli::ontology::Shader` can have vertex and fragment shaders for
// computing the distances between nodes of a graph. Then, each `ComputeTask`
// would contain the graph data, eg. as a distance matrix. Then, rendering a
// `ComputeTask` means computing that task.
//
// Rendering a `ComputeTask` is done by iterating the task until either
// `end_condition_callback_engine->execute()` returns `true`, or until
// `n_max_iterations` is reached. If `end_condition_callback_engine` is `nullptr`
// or `end_condition_callback_engine->execute()` does not not return an `AnyValue`
// which contains `datatypes::BOOL`, then `end_condition_callback_engine` is ignored
// and `n_max_iterations` is the exact number of iterations to be done. However,
// even if `end_condition_callback_engine->execute()` would return an invalid return
// value, that is, not an `AnyValue` which contains `datatypes::BOOL`,
// `end_condition_callback_engine->execute()` is still called and taken into account
// in every iteration.
//
// When iterating, there is a `PreIterateCallback` which is executed before each iteration,
// and also a `PostIterateCallback` which is executed correspondingly after each iteration.
// Of course `PreRenderCallback` and `PostRenderCallback` can be used as well.
// `PreRenderCallback` gets executed before the first `PreIterateCallback` call, and
// `PostRenderCallback` gets executed after the last `PostRenderCallback` call.
// All these callbacks are optional and can be left to `nullptr` if they are not needed.

namespace yli
{
    namespace callback_system
    {
        class CallbackEngine;
    }

    namespace ontology
    {
        class Shader;

        class ComputeTask: public yli::ontology::Entity
        {
            public:
                ComputeTask(yli::ontology::Universe* const universe, const ComputeTaskStruct& compute_task_struct)
                    : Entity(universe)
                {
                    // constructor.
                    this->texture_file_format = compute_task_struct.texture_file_format;
                    this->texture_filename = compute_task_struct.texture_filename;
                    this->output_filename = compute_task_struct.output_filename;
                    this->parent = compute_task_struct.parent;
                    this->end_condition_callback_engine = compute_task_struct.end_condition_callback_engine;

                    this->result_vector = nullptr;

                    this->n_max_iterations = compute_task_struct.n_max_iterations;
                    this->compute_taskID = compute_task_struct.compute_taskID;
                    this->texture_width = compute_task_struct.texture_width;
                    this->texture_height = compute_task_struct.texture_height;

                    // variables related to the framebuffer.
                    this->framebuffer                  = 0; // some dummy value.
                    this->source_texture               = 0; // some dummy value.
                    this->target_texture               = 0; // some dummy value.
                    this->openGL_textureID             = 0; // some dummy value.
                    this->is_framebuffer_initialized   = false;
                    this->is_ready                     = false;

                    this->vertex_position_modelspaceID = 0; // some dummy value.
                    this->vertexUVID                   = 0; // some dummy value.
                    this->vertexbuffer                 = 0; // some dummy value.
                    this->uvbuffer                     = 0; // some dummy value.

                    this->format                       = compute_task_struct.format;
                    this->type                         = compute_task_struct.type;

                    this->preiterate_callback = compute_task_struct.preiterate_callback;
                    this->postiterate_callback = compute_task_struct.postiterate_callback;

                    // Get `childID` from `Shader` and set pointer to this `ComputeTask`.
                    this->bind_to_parent();

                    // Get a handle for our buffers.
                    this->vertex_position_modelspaceID = glGetAttribLocation(this->parent->get_programID(), "vertex_position_modelspace");
                    this->vertexUVID = glGetAttribLocation(this->parent->get_programID(), "vertexUV");

                    glUseProgram(this->parent->get_programID());

                    // Load the source texture, just like in `yli::ontology::Material` constructor.
                    if (this->texture_file_format == "bmp" || this->texture_file_format == "BMP")
                    {
                        if (!yli::load::load_BMP_texture(this->texture_filename, this->texture_width, this->texture_height, this->texture_size, this->source_texture))
                        {
                            std::cerr << "ERROR: loading BMP texture failed!\n";
                        }
                    }
                    else if (this->texture_file_format == "dds" || this->texture_file_format == "DDS")
                    {
                        if (!yli::load::load_DDS_texture(this->texture_filename, this->texture_width, this->texture_height, this->texture_size, this->source_texture))
                        {
                            std::cerr << "ERROR: loading DDS texture failed!\n";
                        }
                    }
                    else
                    {
                        std::cerr << "no texture was loaded!\n";
                        std::cerr << "texture file format: " << this->texture_file_format << "\n";
                    }

                    // Get a handle for our "my_texture_sampler" uniform.
                    this->openGL_textureID = glGetUniformLocation(this->parent->get_programID(), "my_texture_sampler");

                    // Create model (a square which consists of 2 triangles).
                    // *---*
                    // |  /|
                    // | / |
                    // |/  |
                    // *---*
                    const std::vector<glm::vec2> vertices
                    { { 1.0f, 1.0f }, { 1.0f, -1.0f }, { -1.0f, -1.0f }, { -1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
                    this->vertices_size = vertices.size();

                    const std::vector<glm::vec2> uvs
                    { { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f } };
                    this->uvs_size = uvs.size();

                    // Load model into a VBO.

                    // Vertices.
                    glGenBuffers(1, &this->vertexbuffer);
                    glBindBuffer(GL_ARRAY_BUFFER, this->vertexbuffer);
                    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec2), &vertices[0], GL_STATIC_DRAW);

                    // UVs.
                    glGenBuffers(1, &this->uvbuffer);
                    glBindBuffer(GL_ARRAY_BUFFER, this->uvbuffer);
                    glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);

                    // `yli::ontology::Entity` member variables begin here.
                    this->type_string = "yli::ontology::ComputeTask*";
                    this->can_be_erased = true;
                }

                ComputeTask(const ComputeTask&) = delete;            // Delete copy constructor.
                ComputeTask &operator=(const ComputeTask&) = delete; // Delete copy assignment.

                // destructor.
                ~ComputeTask();

                yli::ontology::Entity* get_parent() const override;

                template<class T1>
                    friend void yli::hierarchy::bind_child_to_parent(T1 child_pointer, std::vector<T1>& child_pointer_vector, std::queue<std::size_t>& free_childID_queue, std::size_t& number_of_children);
                template<class T1>
                    friend std::size_t yli::ontology::get_number_of_descendants(const std::vector<T1>& child_pointer_vector);
                template<class T1>
                    friend void yli::ontology::render_children(const std::vector<T1>& child_pointer_vector);

            private:
                void bind_to_parent();

                // This method renders this `ComputeTask`, that is, computes this task.
                void render();

                void preiterate() const;
                void postiterate() const;

                std::size_t get_number_of_children() const override;
                std::size_t get_number_of_descendants() const override;

                std::string texture_file_format; // Type of the texture file. supported file formats so far: `"bmp"`/`"BMP"`, `"dds"`/`"DDS"`.
                std::string texture_filename;    // Filename of the model file.
                std::string output_filename;     // Filename of the output file.

                yli::ontology::Shader* parent; // pointer to the `Shader`.

                // End iterating when `end_condition_callback_engine` returns `true`.
                std::shared_ptr<yli::callback_system::CallbackEngine> end_condition_callback_engine;

                std::shared_ptr<std::vector<uint8_t>> result_vector;

                // This is the maximum number of iterations.
                // If `end_condition_callback_engine` is `nullptr`, then this is the number of iterations.
                // If `end_condition_callback_engine` is not `nullptr`, then this is the maximum number of iterations.
                std::size_t n_max_iterations;

                std::size_t compute_taskID;

                std::size_t texture_width;
                std::size_t texture_height;
                std::size_t texture_size;

                std::size_t vertices_size;
                std::size_t uvs_size;

                // variables related to the framebuffer.
                uint32_t framebuffer;
                uint32_t source_texture;
                uint32_t target_texture;
                uint32_t openGL_textureID;           // Texture ID, returned by `glGetUniformLocation(this->parent->get_programID(), "my_texture_sampler")`.
                bool is_framebuffer_initialized;
                bool is_ready;

                uint32_t vertex_position_modelspaceID;
                uint32_t vertexUVID;

                uint32_t vertexbuffer;
                uint32_t uvbuffer;

                GLenum format;
                GLenum type;

                PreIterateCallback preiterate_callback;
                PostIterateCallback postiterate_callback;
        };
    }
}

#endif
<commit_msg>Bugfix: `yli::ontology::ComputeTask` constructor: vertex order.<commit_after>#ifndef __COMPUTE_TASK_HPP_INCLUDED
#define __COMPUTE_TASK_HPP_INCLUDED

#include "entity.hpp"
#include "shader.hpp"
#include "compute_task_struct.hpp"
#include "pre_iterate_callback.hpp"
#include "post_iterate_callback.hpp"
#include "render_templates.hpp"
#include "family_templates.hpp"
#include "code/ylikuutio/load/texture_loader.hpp"
#include "code/ylikuutio/opengl/opengl.hpp"
#include "code/ylikuutio/hierarchy/hierarchy_templates.hpp"

// Include GLEW
#include "code/ylikuutio/opengl/ylikuutio_glew.hpp" // GLfloat, GLuint etc.

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cstddef>  // std::size_t
#include <iostream> // std::cout, std::cin, std::cerr
#include <memory>   // std::make_shared, std::shared_ptr
#include <queue>    // std::queue
#include <stdint.h> // uint32_t etc.
#include <vector>   // std::vector

// `yli::ontology::ComputeTask` is a class which contains the data for a single
// computing task. `ComputeTask` does not have the OpenGL shaders used to process
// the data. Instead, the shaders are contained by the parent `Entity` which is
// an `yli::ontology::ComputeTask` instance.
//
// For example, `yli::ontology::Shader` can have vertex and fragment shaders for
// computing the distances between nodes of a graph. Then, each `ComputeTask`
// would contain the graph data, eg. as a distance matrix. Then, rendering a
// `ComputeTask` means computing that task.
//
// Rendering a `ComputeTask` is done by iterating the task until either
// `end_condition_callback_engine->execute()` returns `true`, or until
// `n_max_iterations` is reached. If `end_condition_callback_engine` is `nullptr`
// or `end_condition_callback_engine->execute()` does not not return an `AnyValue`
// which contains `datatypes::BOOL`, then `end_condition_callback_engine` is ignored
// and `n_max_iterations` is the exact number of iterations to be done. However,
// even if `end_condition_callback_engine->execute()` would return an invalid return
// value, that is, not an `AnyValue` which contains `datatypes::BOOL`,
// `end_condition_callback_engine->execute()` is still called and taken into account
// in every iteration.
//
// When iterating, there is a `PreIterateCallback` which is executed before each iteration,
// and also a `PostIterateCallback` which is executed correspondingly after each iteration.
// Of course `PreRenderCallback` and `PostRenderCallback` can be used as well.
// `PreRenderCallback` gets executed before the first `PreIterateCallback` call, and
// `PostRenderCallback` gets executed after the last `PostRenderCallback` call.
// All these callbacks are optional and can be left to `nullptr` if they are not needed.

namespace yli
{
    namespace callback_system
    {
        class CallbackEngine;
    }

    namespace ontology
    {
        class Shader;

        class ComputeTask: public yli::ontology::Entity
        {
            public:
                ComputeTask(yli::ontology::Universe* const universe, const ComputeTaskStruct& compute_task_struct)
                    : Entity(universe)
                {
                    // constructor.
                    this->texture_file_format = compute_task_struct.texture_file_format;
                    this->texture_filename = compute_task_struct.texture_filename;
                    this->output_filename = compute_task_struct.output_filename;
                    this->parent = compute_task_struct.parent;
                    this->end_condition_callback_engine = compute_task_struct.end_condition_callback_engine;

                    this->result_vector = nullptr;

                    this->n_max_iterations = compute_task_struct.n_max_iterations;
                    this->compute_taskID = compute_task_struct.compute_taskID;
                    this->texture_width = compute_task_struct.texture_width;
                    this->texture_height = compute_task_struct.texture_height;

                    // variables related to the framebuffer.
                    this->framebuffer                  = 0; // some dummy value.
                    this->source_texture               = 0; // some dummy value.
                    this->target_texture               = 0; // some dummy value.
                    this->openGL_textureID             = 0; // some dummy value.
                    this->is_framebuffer_initialized   = false;
                    this->is_ready                     = false;

                    this->vertex_position_modelspaceID = 0; // some dummy value.
                    this->vertexUVID                   = 0; // some dummy value.
                    this->vertexbuffer                 = 0; // some dummy value.
                    this->uvbuffer                     = 0; // some dummy value.

                    this->format                       = compute_task_struct.format;
                    this->type                         = compute_task_struct.type;

                    this->preiterate_callback = compute_task_struct.preiterate_callback;
                    this->postiterate_callback = compute_task_struct.postiterate_callback;

                    // Get `childID` from `Shader` and set pointer to this `ComputeTask`.
                    this->bind_to_parent();

                    // Get a handle for our buffers.
                    this->vertex_position_modelspaceID = glGetAttribLocation(this->parent->get_programID(), "vertex_position_modelspace");
                    this->vertexUVID = glGetAttribLocation(this->parent->get_programID(), "vertexUV");

                    glUseProgram(this->parent->get_programID());

                    // Load the source texture, just like in `yli::ontology::Material` constructor.
                    if (this->texture_file_format == "bmp" || this->texture_file_format == "BMP")
                    {
                        if (!yli::load::load_BMP_texture(this->texture_filename, this->texture_width, this->texture_height, this->texture_size, this->source_texture))
                        {
                            std::cerr << "ERROR: loading BMP texture failed!\n";
                        }
                    }
                    else if (this->texture_file_format == "dds" || this->texture_file_format == "DDS")
                    {
                        if (!yli::load::load_DDS_texture(this->texture_filename, this->texture_width, this->texture_height, this->texture_size, this->source_texture))
                        {
                            std::cerr << "ERROR: loading DDS texture failed!\n";
                        }
                    }
                    else
                    {
                        std::cerr << "no texture was loaded!\n";
                        std::cerr << "texture file format: " << this->texture_file_format << "\n";
                    }

                    // Get a handle for our "my_texture_sampler" uniform.
                    this->openGL_textureID = glGetUniformLocation(this->parent->get_programID(), "my_texture_sampler");

                    // Create model (a square which consists of 2 triangles).
                    // *---*
                    // |  /|
                    // | / |
                    // |/  |
                    // *---*
                    const std::vector<glm::vec2> vertices
                    { { -1.0f, 1.0f }, { -1.0f, -1.0f }, { 1.0f, 1.0f }, { 1.0f, -1.0f }, { 1.0f, 1.0f }, { -1.0f, -1.0f } };
                    this->vertices_size = vertices.size();

                    const std::vector<glm::vec2> uvs
                    { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 0.0f } };
                    this->uvs_size = uvs.size();

                    // Load model into a VBO.

                    // Vertices.
                    glGenBuffers(1, &this->vertexbuffer);
                    glBindBuffer(GL_ARRAY_BUFFER, this->vertexbuffer);
                    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec2), &vertices[0], GL_STATIC_DRAW);

                    // UVs.
                    glGenBuffers(1, &this->uvbuffer);
                    glBindBuffer(GL_ARRAY_BUFFER, this->uvbuffer);
                    glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);

                    // `yli::ontology::Entity` member variables begin here.
                    this->type_string = "yli::ontology::ComputeTask*";
                    this->can_be_erased = true;
                }

                ComputeTask(const ComputeTask&) = delete;            // Delete copy constructor.
                ComputeTask &operator=(const ComputeTask&) = delete; // Delete copy assignment.

                // destructor.
                ~ComputeTask();

                yli::ontology::Entity* get_parent() const override;

                template<class T1>
                    friend void yli::hierarchy::bind_child_to_parent(T1 child_pointer, std::vector<T1>& child_pointer_vector, std::queue<std::size_t>& free_childID_queue, std::size_t& number_of_children);
                template<class T1>
                    friend std::size_t yli::ontology::get_number_of_descendants(const std::vector<T1>& child_pointer_vector);
                template<class T1>
                    friend void yli::ontology::render_children(const std::vector<T1>& child_pointer_vector);

            private:
                void bind_to_parent();

                // This method renders this `ComputeTask`, that is, computes this task.
                void render();

                void preiterate() const;
                void postiterate() const;

                std::size_t get_number_of_children() const override;
                std::size_t get_number_of_descendants() const override;

                std::string texture_file_format; // Type of the texture file. supported file formats so far: `"bmp"`/`"BMP"`, `"dds"`/`"DDS"`.
                std::string texture_filename;    // Filename of the model file.
                std::string output_filename;     // Filename of the output file.

                yli::ontology::Shader* parent; // pointer to the `Shader`.

                // End iterating when `end_condition_callback_engine` returns `true`.
                std::shared_ptr<yli::callback_system::CallbackEngine> end_condition_callback_engine;

                std::shared_ptr<std::vector<uint8_t>> result_vector;

                // This is the maximum number of iterations.
                // If `end_condition_callback_engine` is `nullptr`, then this is the number of iterations.
                // If `end_condition_callback_engine` is not `nullptr`, then this is the maximum number of iterations.
                std::size_t n_max_iterations;

                std::size_t compute_taskID;

                std::size_t texture_width;
                std::size_t texture_height;
                std::size_t texture_size;

                std::size_t vertices_size;
                std::size_t uvs_size;

                // variables related to the framebuffer.
                uint32_t framebuffer;
                uint32_t source_texture;
                uint32_t target_texture;
                uint32_t openGL_textureID;           // Texture ID, returned by `glGetUniformLocation(this->parent->get_programID(), "my_texture_sampler")`.
                bool is_framebuffer_initialized;
                bool is_ready;

                uint32_t vertex_position_modelspaceID;
                uint32_t vertexUVID;

                uint32_t vertexbuffer;
                uint32_t uvbuffer;

                GLenum format;
                GLenum type;

                PreIterateCallback preiterate_callback;
                PostIterateCallback postiterate_callback;
        };
    }
}

#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: javamaker.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2004-10-28 16:20:05 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>

#include "sal/main.h"

#ifndef _CODEMAKER_TYPEMANAGER_HXX_
#include <codemaker/typemanager.hxx>
#endif
#include "codemaker/generatedtypeset.hxx"

#include "javaoptions.hxx"
#include "javatype.hxx"

using namespace rtl;

sal_Bool produceAllTypes(const OString& typeName,
                         TypeManager& typeMgr,
                         codemaker::GeneratedTypeSet & generated,
                         JavaOptions* pOptions,
                         sal_Bool bFullScope)
    throw( CannotDumpException )
{
    if (!produceType(typeName, typeMgr, generated, pOptions))
    {
        fprintf(stderr, "%s ERROR: %s\n",
                pOptions->getProgramName().getStr(),
                OString("cannot dump Type '" + typeName + "'").getStr());
        exit(99);
    }

    RegistryKey typeKey = typeMgr.getTypeKey(typeName);
    RegistryKeyNames subKeys;

    if (typeKey.getKeyNames(OUString(), subKeys))
        return sal_False;

    OString tmpName;
    for (sal_uInt32 i=0; i < subKeys.getLength(); i++)
    {
        tmpName = OUStringToOString(subKeys.getElement(i), RTL_TEXTENCODING_UTF8);

        if (pOptions->isValid("-B"))
            tmpName = tmpName.copy(tmpName.indexOf('/', 1) + 1);
        else
            tmpName = tmpName.copy(1);

        if (bFullScope)
        {
            if (!produceAllTypes(
                    tmpName, typeMgr, generated, pOptions, sal_True))
                return sal_False;
        } else
        {
            if (!produceType(tmpName, typeMgr, generated, pOptions))
                return sal_False;
        }
    }

    return sal_True;
}

SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{
    JavaOptions options;

    try
    {
        if (!options.initOptions(argc, argv))
        {
            exit(1);
        }
    }
    catch( IllegalArgument& e)
    {
        fprintf(stderr, "Illegal option: %s\n", e.m_message.getStr());
        exit(99);
    }

    RegistryTypeManager typeMgr;

    if (!typeMgr.init(options.getInputFiles(), options.getExtraInputFiles()))
    {
        fprintf(stderr, "%s : init registries failed, check your registry files.\n", options.getProgramName().getStr());
        exit(99);
    }

    if (options.isValid("-B"))
    {
        typeMgr.setBase(options.getOption("-B"));
    }

    try
    {
        if (options.isValid("-T"))
        {
            OString tOption(options.getOption("-T"));
            sal_Int32 nIndex = 0;

            codemaker::GeneratedTypeSet generated;
            OString typeName, tmpName;
            sal_Bool ret = sal_False;
            do
            {
                typeName = tOption.getToken(0, ';', nIndex);

                sal_Int32 nPos = typeName.lastIndexOf( '.' );
                tmpName = typeName.copy( nPos != -1 ? nPos+1 : 0 );
                if (tmpName == "*")
                {
                    // produce this type and his scope, but the scope is not recursively  generated.
                    if (typeName.equals("*"))
                    {
                        tmpName = "/";
                    } else
                    {
                        tmpName = typeName.copy(0, typeName.lastIndexOf('.')).replace('.', '/');
                        if (tmpName.getLength() == 0)
                            tmpName = "/";
                        else
                            tmpName.replace('.', '/');
                    }
                    ret = produceAllTypes(
                        tmpName, typeMgr, generated, &options, sal_False);
                } else
                {
                    // produce only this type
                    ret = produceType(
                        typeName.replace('.', '/'), typeMgr, generated,
                        &options);
                }

                if (!ret)
                {
                    fprintf(stderr, "%s ERROR: %s\n",
                            options.getProgramName().getStr(),
                            OString("cannot dump Type '" + typeName + "'").getStr());
                    exit(99);
                }
            } while( nIndex != -1 );
        } else
        {
            // produce all types
            codemaker::GeneratedTypeSet generated;
            if (!produceAllTypes("/", typeMgr, generated, &options, sal_True))
            {
                fprintf(stderr, "%s ERROR: %s\n",
                        options.getProgramName().getStr(),
                        "an error occurs while dumping all types.");
                exit(99);
            }
        }
    }
    catch( CannotDumpException& e)
    {
        fprintf(stderr, "%s ERROR: %s\n",
                options.getProgramName().getStr(),
                e.m_message.getStr());
        exit(99);
    }

    return 0;
}

<commit_msg>INTEGRATION: CWS sdksample (1.4.14); FILE MERGED 2004/11/02 17:14:39 jsc 1.4.14.2: RESYNC: (1.4-1.5); FILE MERGED 2004/07/05 11:03:47 jsc 1.4.14.1: #i30954# use registry keys directly instead of typenames, #116780# genrate full scope<commit_after>/*************************************************************************
 *
 *  $RCSfile: javamaker.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: rt $ $Date: 2005-01-31 15:29:29 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>

#include "sal/main.h"

#ifndef _CODEMAKER_TYPEMANAGER_HXX_
#include <codemaker/typemanager.hxx>
#endif
#include "codemaker/generatedtypeset.hxx"

#include "javaoptions.hxx"
#include "javatype.hxx"

using namespace rtl;

sal_Bool produceAllTypes(RegistryKey& rTypeKey, sal_Bool bIsExtraType,
                         TypeManager const & typeMgr,
                         codemaker::GeneratedTypeSet & generated,
                         JavaOptions* pOptions,
                         sal_Bool bFullScope)
    throw( CannotDumpException )
{
    OString typeName = typeMgr.getTypeName(rTypeKey);

    if (!produceType(rTypeKey, bIsExtraType, typeMgr, generated, pOptions))
    {
        fprintf(stderr, "%s ERROR: %s\n",
                pOptions->getProgramName().getStr(),
                OString("cannot dump Type '" + typeName + "'").getStr());
        exit(99);
    }

    RegistryKeyList typeKeys = typeMgr.getTypeKeys(typeName);
    RegistryKeyList::const_iterator iter = typeKeys.begin();
    RegistryKey key, subKey;
    RegistryKeyArray subKeys;

    while (iter != typeKeys.end())
    {
        key = (*iter).first;

        if (!(*iter).second  && !key.openSubKeys(OUString(), subKeys))
        {
            for (sal_uInt32 i = 0; i < subKeys.getLength(); i++)
            {
                subKey = subKeys.getElement(i);
                if (bFullScope)
                {
                    if (!produceAllTypes(
                            subKey, (*iter).second,
                            typeMgr, generated, pOptions, sal_True))
                        return sal_False;
                } else
                {
                    if (!produceType(subKey, (*iter).second,
                                     typeMgr, generated, pOptions))
                        return sal_False;
                }
            }
        }

        ++iter;
    }

    return sal_True;
}

sal_Bool produceAllTypes(const OString& typeName,
                         TypeManager const & typeMgr,
                         codemaker::GeneratedTypeSet & generated,
                         JavaOptions* pOptions,
                         sal_Bool bFullScope)
    throw( CannotDumpException )
{
    if (!produceType(typeName, typeMgr, generated, pOptions))
    {
        fprintf(stderr, "%s ERROR: %s\n",
                pOptions->getProgramName().getStr(),
                OString("cannot dump Type '" + typeName + "'").getStr());
        exit(99);
    }

    RegistryKeyList typeKeys = typeMgr.getTypeKeys(typeName);
    RegistryKeyList::const_iterator iter = typeKeys.begin();
    RegistryKey key, subKey;
    RegistryKeyArray subKeys;

    while (iter != typeKeys.end())
    {
        key = (*iter).first;
        if (!(*iter).second  && !key.openSubKeys(OUString(), subKeys))
        {
            for (sal_uInt32 i = 0; i < subKeys.getLength(); i++)
            {
                subKey = subKeys.getElement(i);
                if (bFullScope)
                {
                    if (!produceAllTypes(
                            subKey, (*iter).second,
                            typeMgr, generated, pOptions, sal_True))
                        return sal_False;
                } else
                {
                    if (!produceType(subKey, (*iter).second,
                                     typeMgr, generated, pOptions))
                        return sal_False;
                }
            }
        }

        ++iter;
    }

    return sal_True;
}

SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{
    JavaOptions options;

    try
    {
        if (!options.initOptions(argc, argv))
        {
            exit(1);
        }
    }
    catch( IllegalArgument& e)
    {
        fprintf(stderr, "Illegal option: %s\n", e.m_message.getStr());
        exit(99);
    }

    RegistryTypeManager typeMgr;

    if (!typeMgr.init(options.getInputFiles(), options.getExtraInputFiles()))
    {
        fprintf(stderr, "%s : init registries failed, check your registry files.\n", options.getProgramName().getStr());
        exit(99);
    }

    if (options.isValid("-B"))
    {
        typeMgr.setBase(options.getOption("-B"));
    }

    try
    {
        if (options.isValid("-T"))
        {
            OString tOption(options.getOption("-T"));
            sal_Int32 nIndex = 0;

            codemaker::GeneratedTypeSet generated;
            OString typeName, tmpName;
            sal_Bool ret = sal_False;
            do
            {
                typeName = tOption.getToken(0, ';', nIndex);

                sal_Int32 nPos = typeName.lastIndexOf( '.' );
                tmpName = typeName.copy( nPos != -1 ? nPos+1 : 0 );
                if (tmpName == "*")
                {
                    // produce this type and his scope.
                    if (typeName.equals("*"))
                    {
                        tmpName = "/";
                    } else
                    {
                        tmpName = typeName.copy(0, typeName.lastIndexOf('.')).replace('.', '/');
                        if (tmpName.getLength() == 0)
                            tmpName = "/";
                        else
                            tmpName.replace('.', '/');
                    }
                    // related to task #116780# the scope is recursively
                    // generated.  bFullScope = true
                    ret = produceAllTypes(
                        tmpName, typeMgr, generated, &options, sal_True);
                } else
                {
                    // produce only this type
                    ret = produceType(
                        typeName.replace('.', '/'), typeMgr, generated,
                        &options);
                }

                if (!ret)
                {
                    fprintf(stderr, "%s ERROR: %s\n",
                            options.getProgramName().getStr(),
                            OString("cannot dump Type '" + typeName + "'").getStr());
                    exit(99);
                }
            } while( nIndex != -1 );
        } else
        {
            // produce all types
            codemaker::GeneratedTypeSet generated;
            if (!produceAllTypes("/", typeMgr, generated, &options, sal_True))
            {
                fprintf(stderr, "%s ERROR: %s\n",
                        options.getProgramName().getStr(),
                        "an error occurs while dumping all types.");
                exit(99);
            }
        }
    }
    catch( CannotDumpException& e)
    {
        fprintf(stderr, "%s ERROR: %s\n",
                options.getProgramName().getStr(),
                e.m_message.getStr());
        exit(99);
    }

    return 0;
}

<|endoftext|>


<commit_before>// Copyright (c) 2014 Baidu, Inc.G
//
// 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.
//
// Authors: Lei He (helei@qiyi.com)

#include <cmath>
#include <gflags/gflags.h>
#include <butil/time.h>
#include <butil/logging.h>
#include "brpc/circuit_breaker.h"

namespace brpc {

DEFINE_int32(circuit_breaker_short_window_size, 500,
    "Short window sample size.");
DEFINE_int32(circuit_breaker_long_window_size, 1000,
    "Long window sample size.");
DEFINE_int32(circuit_breaker_short_window_error_percent, 10,
    "The maximum error rate allowed by the short window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_long_window_error_percent, 5,
    "The maximum error rate allowed by the long window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_min_error_cost_us, 500,
    "The minimum error_cost, when the ema of error cost is less than this "
    "value, it will be set to zero.");
DEFINE_int32(circuit_breaker_max_failed_latency_mutilple, 2,
    "The maximum multiple of the latency of the failed request relative to "
    "the average latency of the success requests.");
DEFINE_int32(circuit_breaker_min_isolation_duration_ms, 100,
    "Minimum isolation duration in milliseconds");
DEFINE_int32(circuit_breaker_max_isolation_duration_ms, 300000,
    "Maximum isolation duration in milliseconds");

namespace {
// EPSILON is used to generate the smoothing coefficient when calculating EMA.
// The larger the EPSILON, the larger the smoothing coefficient, which means
// that the proportion of early data is larger.
// smooth = pow(EPSILON, 1 / window_size),
// eg: when window_size = 100,
// EPSILON = 0.1, smooth = 0.9772
// EPSILON = 0.3, smooth = 0.9880
// when window_size = 1000,
// EPSILON = 0.1, smooth = 0.9977
// EPSILON = 0.3, smooth = 0.9987
const double EPSILON = 0.1;
}  // namepace

CircuitBreaker::EmaErrorRecorder::EmaErrorRecorder(int window_size,
                                                   int max_error_percent)
    : _window_size(window_size)
    , _max_error_percent(max_error_percent)
    , _smooth(std::pow(EPSILON, 1.0/window_size))
    , _sample_count(0)
    , _ema_error_cost(0)
    , _ema_latency(0)
    , _broken(false) {
}

bool CircuitBreaker::EmaErrorRecorder::OnCallEnd(int error_code,
                                                 int64_t latency) {
    if (_broken.load(butil::memory_order_relaxed)) {
        return false;
    }

    int64_t ema_latency = 0;
    bool healthy = false;
    if (error_code == 0) {
        ema_latency = UpdateLatency(latency);
        healthy = UpdateErrorCost(0, ema_latency);
    } else {
        ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        healthy = UpdateErrorCost(latency, ema_latency);
    }

    if (_sample_count.fetch_add(1, butil::memory_order_relaxed) < _window_size) {
        return true;
    }

    if (!healthy) {
        _broken.store(true, butil::memory_order_relaxed);
    }
    return healthy;
}

void CircuitBreaker::EmaErrorRecorder::Reset() {
    _sample_count.store(0, butil::memory_order_relaxed);
    _ema_error_cost.store(0, butil::memory_order_relaxed);
    _ema_latency.store(0, butil::memory_order_relaxed);
    _broken.store(false, butil::memory_order_relaxed);
}

int64_t CircuitBreaker::EmaErrorRecorder::UpdateLatency(int64_t latency) {
    int64_t ema_latency = _ema_latency.load(butil::memory_order_relaxed);
    do {
        int64_t next_ema_latency = 0;
        if (0 == ema_latency) {
            next_ema_latency = latency;
        } else {
            next_ema_latency = ema_latency * _smooth + latency * (1 - _smooth);
        }
        if (_ema_latency.compare_exchange_weak(ema_latency, next_ema_latency)) {
            return next_ema_latency;
        }
    } while(true);
}

bool CircuitBreaker::EmaErrorRecorder::UpdateErrorCost(int64_t error_cost,
                                                       int64_t ema_latency) {
    const int max_mutilple = FLAGS_circuit_breaker_max_failed_latency_mutilple;
    if (ema_latency != 0) {
        error_cost = std::min(ema_latency * max_mutilple, error_cost);
    }
    //Errorous response
    if (error_cost != 0) {
        int64_t ema_error_cost =
            _ema_error_cost.fetch_add(error_cost, butil::memory_order_relaxed);
        ema_error_cost += error_cost;
        int64_t max_error_cost = ema_latency * _window_size *
            (_max_error_percent / 100.0) * (1.0 + EPSILON);
        return ema_error_cost <= max_error_cost;
    }

    //Ordinary response
    int64_t ema_error_cost = _ema_error_cost.load(butil::memory_order_relaxed);
    do {
        if (ema_error_cost == 0) {
            break;
        } else if (ema_error_cost < FLAGS_circuit_breaker_min_error_cost_us) {
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, 0, butil::memory_order_relaxed)) {
                break;
            }
        } else {
            int64_t next_ema_error_cost = ema_error_cost * _smooth;
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, next_ema_error_cost)) {
                break;
            }
        }
    } while (true);
    return true;
}

CircuitBreaker::CircuitBreaker()
    : _long_window(FLAGS_circuit_breaker_long_window_size,
                   FLAGS_circuit_breaker_long_window_error_percent)
    , _short_window(FLAGS_circuit_breaker_short_window_size,
                    FLAGS_circuit_breaker_short_window_error_percent)
    , _last_reset_time_ms(butil::cpuwide_time_ms())
    , _broken(false)
    , _isolation_duration_ms(FLAGS_circuit_breaker_min_isolation_duration_ms) {
}

bool CircuitBreaker::OnCallEnd(int error_code, int64_t latency) {
    if(_long_window.OnCallEnd(error_code, latency) &&
           _short_window.OnCallEnd(error_code, latency)) {
        return true;
    } else {
        UpdateIsolationDuration();
        return false;
    }
}

void CircuitBreaker::Reset() {
    _long_window.Reset();
    _short_window.Reset();
    _last_reset_time_ms = butil::cpuwide_time_ms();
    _broken.store(false, butil::memory_order_release);
}

void CircuitBreaker::UpdateIsolationDuration() {
    if (!_broken.load(butil::memory_order_relaxed) &&
        !_broken.exchange(true, butil::memory_order_acquire)) {
        int64_t now_time_ms = butil::cpuwide_time_ms();
        int isolation_duration_ms = _isolation_duration_ms.load(butil::memory_order_relaxed);
        const int max_isolation_duration_ms =
            FLAGS_circuit_breaker_max_isolation_duration_ms;
        const int min_isolation_duration_ms =
            FLAGS_circuit_breaker_min_isolation_duration_ms;
        if (now_time_ms - _last_reset_time_ms < max_isolation_duration_ms) {
            isolation_duration_ms *= 2;
            isolation_duration_ms =
                std::min(isolation_duration_ms, max_isolation_duration_ms);
        } else {
            isolation_duration_ms = min_isolation_duration_ms;
        }
        _isolation_duration_ms.store(isolation_duration_ms, butil::memory_order_relaxed);
    }
}

}  // namespace brpc
<commit_msg>delete unnecessary include<commit_after>// Copyright (c) 2014 Baidu, Inc.G
//
// 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.
//
// Authors: Lei He (helei@qiyi.com)

#include <cmath>
#include <gflags/gflags.h>
#include <butil/time.h>
#include "brpc/circuit_breaker.h"

namespace brpc {

DEFINE_int32(circuit_breaker_short_window_size, 500,
    "Short window sample size.");
DEFINE_int32(circuit_breaker_long_window_size, 1000,
    "Long window sample size.");
DEFINE_int32(circuit_breaker_short_window_error_percent, 10,
    "The maximum error rate allowed by the short window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_long_window_error_percent, 5,
    "The maximum error rate allowed by the long window, ranging from 0-99.");
DEFINE_int32(circuit_breaker_min_error_cost_us, 500,
    "The minimum error_cost, when the ema of error cost is less than this "
    "value, it will be set to zero.");
DEFINE_int32(circuit_breaker_max_failed_latency_mutilple, 2,
    "The maximum multiple of the latency of the failed request relative to "
    "the average latency of the success requests.");
DEFINE_int32(circuit_breaker_min_isolation_duration_ms, 100,
    "Minimum isolation duration in milliseconds");
DEFINE_int32(circuit_breaker_max_isolation_duration_ms, 300000,
    "Maximum isolation duration in milliseconds");

namespace {
// EPSILON is used to generate the smoothing coefficient when calculating EMA.
// The larger the EPSILON, the larger the smoothing coefficient, which means
// that the proportion of early data is larger.
// smooth = pow(EPSILON, 1 / window_size),
// eg: when window_size = 100,
// EPSILON = 0.1, smooth = 0.9772
// EPSILON = 0.3, smooth = 0.9880
// when window_size = 1000,
// EPSILON = 0.1, smooth = 0.9977
// EPSILON = 0.3, smooth = 0.9987
const double EPSILON = 0.1;
}  // namepace

CircuitBreaker::EmaErrorRecorder::EmaErrorRecorder(int window_size,
                                                   int max_error_percent)
    : _window_size(window_size)
    , _max_error_percent(max_error_percent)
    , _smooth(std::pow(EPSILON, 1.0/window_size))
    , _sample_count(0)
    , _ema_error_cost(0)
    , _ema_latency(0)
    , _broken(false) {
}

bool CircuitBreaker::EmaErrorRecorder::OnCallEnd(int error_code,
                                                 int64_t latency) {
    if (_broken.load(butil::memory_order_relaxed)) {
        return false;
    }

    int64_t ema_latency = 0;
    bool healthy = false;
    if (error_code == 0) {
        ema_latency = UpdateLatency(latency);
        healthy = UpdateErrorCost(0, ema_latency);
    } else {
        ema_latency = _ema_latency.load(butil::memory_order_relaxed);
        healthy = UpdateErrorCost(latency, ema_latency);
    }

    if (_sample_count.fetch_add(1, butil::memory_order_relaxed) < _window_size) {
        return true;
    }

    if (!healthy) {
        _broken.store(true, butil::memory_order_relaxed);
    }
    return healthy;
}

void CircuitBreaker::EmaErrorRecorder::Reset() {
    _sample_count.store(0, butil::memory_order_relaxed);
    _ema_error_cost.store(0, butil::memory_order_relaxed);
    _ema_latency.store(0, butil::memory_order_relaxed);
    _broken.store(false, butil::memory_order_relaxed);
}

int64_t CircuitBreaker::EmaErrorRecorder::UpdateLatency(int64_t latency) {
    int64_t ema_latency = _ema_latency.load(butil::memory_order_relaxed);
    do {
        int64_t next_ema_latency = 0;
        if (0 == ema_latency) {
            next_ema_latency = latency;
        } else {
            next_ema_latency = ema_latency * _smooth + latency * (1 - _smooth);
        }
        if (_ema_latency.compare_exchange_weak(ema_latency, next_ema_latency)) {
            return next_ema_latency;
        }
    } while(true);
}

bool CircuitBreaker::EmaErrorRecorder::UpdateErrorCost(int64_t error_cost,
                                                       int64_t ema_latency) {
    const int max_mutilple = FLAGS_circuit_breaker_max_failed_latency_mutilple;
    if (ema_latency != 0) {
        error_cost = std::min(ema_latency * max_mutilple, error_cost);
    }
    //Errorous response
    if (error_cost != 0) {
        int64_t ema_error_cost =
            _ema_error_cost.fetch_add(error_cost, butil::memory_order_relaxed);
        ema_error_cost += error_cost;
        int64_t max_error_cost = ema_latency * _window_size *
            (_max_error_percent / 100.0) * (1.0 + EPSILON);
        return ema_error_cost <= max_error_cost;
    }

    //Ordinary response
    int64_t ema_error_cost = _ema_error_cost.load(butil::memory_order_relaxed);
    do {
        if (ema_error_cost == 0) {
            break;
        } else if (ema_error_cost < FLAGS_circuit_breaker_min_error_cost_us) {
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, 0, butil::memory_order_relaxed)) {
                break;
            }
        } else {
            int64_t next_ema_error_cost = ema_error_cost * _smooth;
            if (_ema_error_cost.compare_exchange_weak(
                ema_error_cost, next_ema_error_cost)) {
                break;
            }
        }
    } while (true);
    return true;
}

CircuitBreaker::CircuitBreaker()
    : _long_window(FLAGS_circuit_breaker_long_window_size,
                   FLAGS_circuit_breaker_long_window_error_percent)
    , _short_window(FLAGS_circuit_breaker_short_window_size,
                    FLAGS_circuit_breaker_short_window_error_percent)
    , _last_reset_time_ms(butil::cpuwide_time_ms())
    , _broken(false)
    , _isolation_duration_ms(FLAGS_circuit_breaker_min_isolation_duration_ms) {
}

bool CircuitBreaker::OnCallEnd(int error_code, int64_t latency) {
    if(_long_window.OnCallEnd(error_code, latency) &&
           _short_window.OnCallEnd(error_code, latency)) {
        return true;
    } else {
        UpdateIsolationDuration();
        return false;
    }
}

void CircuitBreaker::Reset() {
    _long_window.Reset();
    _short_window.Reset();
    _last_reset_time_ms = butil::cpuwide_time_ms();
    _broken.store(false, butil::memory_order_release);
}

void CircuitBreaker::UpdateIsolationDuration() {
    if (!_broken.load(butil::memory_order_relaxed) &&
        !_broken.exchange(true, butil::memory_order_acquire)) {
        int64_t now_time_ms = butil::cpuwide_time_ms();
        int isolation_duration_ms = _isolation_duration_ms.load(butil::memory_order_relaxed);
        const int max_isolation_duration_ms =
            FLAGS_circuit_breaker_max_isolation_duration_ms;
        const int min_isolation_duration_ms =
            FLAGS_circuit_breaker_min_isolation_duration_ms;
        if (now_time_ms - _last_reset_time_ms < max_isolation_duration_ms) {
            isolation_duration_ms *= 2;
            isolation_duration_ms =
                std::min(isolation_duration_ms, max_isolation_duration_ms);
        } else {
            isolation_duration_ms = min_isolation_duration_ms;
        }
        _isolation_duration_ms.store(isolation_duration_ms, butil::memory_order_relaxed);
    }
}

}  // namespace brpc
<|endoftext|>