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

  Program:   Visualization Toolkit
  Module:    vtkTriangleFilter.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkTriangleFilter.h"
#include "vtkPolygon.h"
#include "vtkTriangleStrip.h"

void vtkTriangleFilter::Execute()
{
  vtkPolyData *input=(vtkPolyData *)this->Input;
  vtkCellArray *inPolys=input->GetPolys();
  vtkCellArray *inStrips=input->GetStrips();;
  int npts, *pts;
  vtkCellArray *newPolys;
  int numCells;
  vtkPolygon poly;
  int i, j;
  vtkIdList outVerts(3*VTK_CELL_SIZE);
  vtkPoints *inPoints=input->GetPoints();
  vtkPointData *pd;
  vtkPolyData *output=(vtkPolyData *)this->Output;

  vtkDebugMacro(<<"Executing triangle filter");

  newPolys = vtkCellArray::New();
  // approximation
  numCells = input->GetNumberOfPolys() + input->GetNumberOfStrips();
  newPolys->Allocate(newPolys->EstimateSize(numCells,3),3*numCells);

  // pass through triangles; triangulate polygons if necessary
  for (inPolys->InitTraversal(); inPolys->GetNextCell(npts,pts); )
    {
    if ( npts == 3 )
      {
      newPolys->InsertNextCell(npts,pts);
      }
    else if ( npts > 3 ) // triangulate poly
      {
      poly.Initialize(npts,pts,inPoints);
      poly.Triangulate(outVerts);
      for (i=0; i<outVerts.GetNumberOfIds()/3; i++)
        {
        newPolys->InsertNextCell(3);
        for (j=0; j<3; j++)
          newPolys->InsertCellPoint(outVerts.GetId(3*i+j));
        }
      }
    }

  if ( inStrips->GetNumberOfCells() > 0 )
    {
    vtkTriangleStrip strip;
    strip.DecomposeStrips(inStrips,newPolys);
    }
//
// Update ourselves
//
  newPolys->Squeeze();
  output->SetPolys(newPolys);
  newPolys->Delete();

  // pass through points and point data
  output->SetPoints(input->GetPoints());
  pd = input->GetPointData();
  output->GetPointData()->PassData(input->GetPointData());

  // pass through other stuff if requested
  if ( this->PassVerts ) output->SetVerts(input->GetVerts());
  if ( this->PassLines ) output->SetLines(input->GetLines());

  vtkDebugMacro(<<"Converted " << inPolys->GetNumberOfCells() <<
               " polygons and " << inStrips->GetNumberOfCells() <<
               " strips to " << newPolys->GetNumberOfCells() <<
               " triangles");
}

void vtkTriangleFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkPolyDataToPolyDataFilter::PrintSelf(os,indent);

  os << indent << "Pass Verts: " << (this->PassVerts ? "On\n" : "Off\n");
  os << indent << "Pass Lines: " << (this->PassLines ? "On\n" : "Off\n");

}

<commit_msg>ENH:Added progress/abort mechanism<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTriangleFilter.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkTriangleFilter.h"
#include "vtkPolygon.h"
#include "vtkTriangleStrip.h"

void vtkTriangleFilter::Execute()
{
  vtkPolyData *input=(vtkPolyData *)this->Input;
  vtkCellArray *inPolys=input->GetPolys();
  vtkCellArray *inStrips=input->GetStrips();;
  int npts, *pts;
  vtkCellArray *newPolys;
  int numCells, cellNum;

  vtkPolygon poly;
  int i, j;
  vtkIdList outVerts(3*VTK_CELL_SIZE);
  vtkPoints *inPoints=input->GetPoints();
  vtkPointData *pd;
  vtkPolyData *output=(vtkPolyData *)this->Output;

  vtkDebugMacro(<<"Executing triangle filter");

  newPolys = vtkCellArray::New();
  // approximation
  numCells = input->GetNumberOfPolys() + input->GetNumberOfStrips();
  newPolys->Allocate(newPolys->EstimateSize(numCells,3),3*numCells);

  // pass through triangles; triangulate polygons if necessary
  for (cellNum=0, inPolys->InitTraversal(); inPolys->GetNextCell(npts,pts); cellNum++)
    {
    if ( npts == 3 )
      {
      newPolys->InsertNextCell(npts,pts);
      }
    else if ( npts > 3 ) // triangulate poly
      {
      poly.Initialize(npts,pts,inPoints);
      poly.Triangulate(outVerts);
      for (i=0; i<outVerts.GetNumberOfIds()/3; i++)
        {
        newPolys->InsertNextCell(3);
        for (j=0; j<3; j++)
          newPolys->InsertCellPoint(outVerts.GetId(3*i+j));
        }
      }

    if ( ! (cellNum % 5000) ) //manage progress reports / early abort
      {
      this->UpdateProgress (cellNum / numCells);
      if ( this->GetAbortExecute() ) break;
      }
    }//for each polygon

  if ( inStrips->GetNumberOfCells() > 0 )
    {
    vtkTriangleStrip strip;
    strip.DecomposeStrips(inStrips,newPolys);
    }

  this->UpdateProgress (1.0);
//
// Update ourselves
//
  newPolys->Squeeze();
  output->SetPolys(newPolys);
  newPolys->Delete();

  // pass through points and point data
  output->SetPoints(input->GetPoints());
  pd = input->GetPointData();
  output->GetPointData()->PassData(input->GetPointData());

  // pass through other stuff if requested
  if ( this->PassVerts ) output->SetVerts(input->GetVerts());
  if ( this->PassLines ) output->SetLines(input->GetLines());

  vtkDebugMacro(<<"Converted " << inPolys->GetNumberOfCells() <<
               " polygons and " << inStrips->GetNumberOfCells() <<
               " strips to " << newPolys->GetNumberOfCells() <<
               " triangles");
}

void vtkTriangleFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkPolyDataToPolyDataFilter::PrintSelf(os,indent);

  os << indent << "Pass Verts: " << (this->PassVerts ? "On\n" : "Off\n");
  os << indent << "Pass Lines: " << (this->PassLines ? "On\n" : "Off\n");

}

<|endoftext|>


<commit_before>#include "frost.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "util.h"
#include "plugin_factory.h"
#include "fetcher.h"
#include <math.h>
#include <NFmiLocation.h>
#include <NFmiMetTime.h>

using namespace std;
using namespace himan;
using namespace himan::plugin;

const string itsName("frost");

const param FParam("FROST-PROB");

frost::frost()
{
	itsLogger = logger("itsName");
}

void frost::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({FParam});

	Start();
}

void frost::Calculate(shared_ptr<info<double>> myTargetInfo, unsigned short threadIndex)
{
	NFmiMetTime theTime(static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%Y"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%m"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%d"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%H"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%M"))));

	const param TParam("T-K");
	const param TDParam("TD-K");
	const param TGParam("TG-K");
	const param WGParam("FFG-MS");
	const param T0Param("PROB-TC-0");
	const param NParam("N-0TO1");
	const param RADParam("RADGLO-WM2");
	//const param LCParam("LC-0TO1");
	//const param ICNParam("ICNCT-PRCNT");

	auto myThreadedLogger = logger("frostThread #" + to_string(threadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + ", level " +
			static_cast<string>(forecastLevel));

	auto cnf = make_shared<plugin_configuration>(*itsConfiguration);
	auto f = GET_PLUGIN(fetcher);

	cnf->SourceProducers({producer(131, 98, 150, "ECG")});

        const std::vector<std::string>& names = { "ECEUR0100", "ECGLO0100", "ECEUR0200" };
	cnf->SourceGeomNames(names);

	info_t TInfo = Fetch(forecastTime, level(kGround, 0), TParam, forecastType, false);
	info_t TDInfo = Fetch(forecastTime, level(kGround, 0), TDParam, forecastType, false);
	info_t TGInfo = Fetch(forecastTime, level(kGroundDepth, 0, 7), TGParam, forecastType, false);
	info_t WGInfo = Fetch(forecastTime, level(kGround, 0), WGParam, forecastType, false);
	info_t NInfo = Fetch(forecastTime, level(kGround, 0), NParam, forecastType, false);
        //info_t LCInfo = Fetch(forecastTime, level(kGround, 0), LCParam, forecastType, false);

        info_t RADInfo;

        try
        {
                cnf->SourceProducers({producer(240, 86, 240, "ECGMTA")});
                RADInfo = f->Fetch(cnf, forecastTime, level(kHeight, 0), RADParam, forecastType, false);
        }
        catch (HPExceptionType& e)
        {
                if  (e == kFileDataNotFound)
                {
                        myThreadedLogger.Error("No data found.");
                }
                return;
        }

	info_t T0ECInfo;

	forecast_type stat_type = forecast_type(kStatisticalProcessing);

	try
	{
		cnf->SourceProducers({producer(242, 86, 242, "ECM_PROB")});
		cnf->SourceGeomNames({"ECEUR0200"});
		T0ECInfo = f->Fetch(cnf, forecastTime, level(kGround, 0), T0Param, stat_type, false);
	}
	catch (HPExceptionType& e)
	{
		if  (e == kFileDataNotFound)
		{
			myThreadedLogger.Error("No data found.");
		}
		return;
	}

	info_t T0MEPSInfo;

	try
	{
		cnf->SourceProducers({producer(260, 86, 204, "MEPSMTA")});
		cnf->SourceGeomNames({"MEPS2500D"});
		T0MEPSInfo = f->Fetch(cnf, forecastTime, level(kHeight, 2), T0Param, stat_type, false);
	}
	catch (HPExceptionType& e)
	{
                if  (e == kFileDataNotFound)
                {
                        myThreadedLogger.Error("No data found.");
                }
                return;
	}

	/*info_t ICNInfo;

	try
	{
		cnf->SourceProducers({producer(150, 86, 150, "HBM_EC")});
		cnf->SourceGeomNames({"HBM"});
		ICNInfo = f->Fetch(cnf, forecastTime, level(kHeight, 0), ICNParam, forecastType, false);
	}
	        catch (HPExceptionType& e)
        {
                if  (e == kFileDataNotFound)
                {
                        myThreadedLogger.Error("No data found.");
                }
                return;
	}*/

	if (!TInfo || !TDInfo || !TGInfo || !WGInfo || !NInfo || !RADInfo || !T0ECInfo || !T0MEPSInfo) // LCINfo, ICNInfo removed.
	{
		myThreadedLogger.Warning("Skipping step " + static_cast<string>(forecastTime.Step()) + ", level " +
			static_cast<string>(forecastLevel));
		return;
	}

	string deviceType = "CPU";

	LOCKSTEP(myTargetInfo, TInfo, TDInfo, TGInfo, WGInfo, NInfo, RADInfo, T0ECInfo, T0MEPSInfo) // LCInfo, ICNInfo, removed.

	{
		double T = TInfo->Value() - himan::constants::kKelvin;
		double TD = TDInfo->Value() - himan::constants::kKelvin;
		double TG = TGInfo->Value() - himan::constants::kKelvin;
		double WG = WGInfo->Value();
		double N = NInfo->Value();
		double RAD = RADInfo->Value();
		double T0EC = T0ECInfo->Value();
		double T0MEPS = T0MEPSInfo->Value();
		//double LC = TInfo->Value();
		//double ICN = TInfo->Value();

		if (IsMissingValue({T, TD, TG, WG, N, RAD, T0EC, T0MEPS})) // LC, ICN
		{
			continue;
		}

		// Calculating indexes and coefficients.

		// dewIndex

		double dewIndex = kHPMissingValue;

		if (TD < -5.0)
		{
			dewIndex = 1.0;
		}

		else if (TD > 5.0)
		{
			dewIndex = 0.0;
		}

		else
		{
			dewIndex = fabs((TD - 5.0) / 10.0);  // TD -5...5
		}

		// nIndex

		double nIndex = kHPMissingValue;

		nIndex = 1.0 - N;

		// tIndexHigh

		double tIndexHigh = kHPMissingValue;

		if (T < 2.5)
		{
			tIndexHigh = 1;
		}

		else if (T > 15.0)
		{
			tIndexHigh = 0;
		}

		else
		{
			tIndexHigh = (fabs((T - 15.0) / 12.5)) * (fabs((T - 15.0) / 12.5));
		}

		// wgWind

		double wgWind = kHPMissingValue;

		if (WG < 1.0)
		{
			wgWind = 1.0;
		}

		else if (WG > 6.0)
		{
			wgWind = 0;
		}

		else
		{
			wgWind = fabs((WG - 6.0) / 5.0);
		}

		double lowWindCoef = 1.0;
		double nCoef = 1.0;
		double weight = 4.0;
		double stabCoef = 1.5 * wgWind + 1.0;

		// Adjusting coefficients when T above and near zero.

		if (T >= 0 && T < 2.5)
		{
			lowWindCoef = fabs((T - 2.5) / 2.5) * weight + 1.0;
			nCoef = 1.0 / lowWindCoef;
		}

		// Calculating frost probability.

		double frost_prob = kHPMissingValue;

		if (T < -3.0)
		{
			frost_prob = 100.0;
		}

		else if (T < 0)
		{
			frost_prob = 90.0;
		}

		else
		{
			frost_prob = ((lowWindCoef * dewIndex) + stabCoef + (nCoef * nIndex)) /
				(lowWindCoef + stabCoef + (1.0 / lowWindCoef)) * 100 * tIndexHigh;
		}

		// Raising the frost probability due to ground temperature TG.

		double tgModel = kHPMissingValue;

		if (T < 5)
		{

			if (TG < -6.0)
			{
				tgModel = 100.0;
			}

			else if (TG > 5.0)
			{
				tgModel = 0;
			}

			else
			{
				tgModel = sqrt(fabs((TG - 5.0) / 11.0)) * 100;
			}

		}

		if (frost_prob < tgModel)
		{
			frost_prob = tgModel;
		}

		// Raising the frost probability due to probability of T<0 and T. Both EC and MEPS cases.

		if (T0EC > 60.0 && T < 5.0 && frost_prob < T0EC)
		{
			frost_prob = T0EC;
		}

		if (frost_prob < T0MEPS && T0MEPS > 40.0)
		{
			frost_prob = (frost_prob * 2.0 + T0MEPS) / 3.0;
		}

		// No frost when radiation is high enough. Valid in  1.4.-15.9.

		int month = stoi(forecastTime.ValidDateTime().String("%m"));
		int day = stoi(forecastTime.ValidDateTime().String("%d"));

		if ((month >= 4 && month <= 8) || (month == 9 && day <= 15))
		{
			if (RAD > 175)
			{
				frost_prob = 0;
			}

		}

		// Lowering frost probability due to sun's elevation angle. Valid in 1.4.-15.9.

		if ((month >= 4 && month <= 8) || (month == 9 && day <= 15))
		{
			NFmiLocation theLocation(myTargetInfo->LatLon().X(), myTargetInfo->LatLon().Y());

                	double elevationAngle = theLocation.ElevationAngle(theTime);
                	double angleCoef = kHPMissingValue;

			if (elevationAngle < -1.0)
                	{
                        	angleCoef = 1;
                	}

                	else if (elevationAngle > 20.0)
                	{
                        	angleCoef = 0;
                	}

                	else
                	{
                        	angleCoef = fabs((elevationAngle - 20.0) / 21.0);
                	}

			frost_prob = angleCoef * frost_prob;
		}

		// No frost probability on sea when there is no ice.

		/*if (ICN == 0 &&  LC == 0)
		{
			frost_prob = 0;
		}*/

		// Lowering frost probability when forecasted T is high enough.

		if (T > 6)
		{
			if (T > 15.0)
			{
				frost_prob = 0;
			}
			else
			{
				frost_prob = frost_prob * fabs((T - 15.0) / 9.0);
			}
		}

		myTargetInfo->Value(frost_prob);

	}

	myThreadedLogger.Info("[" + deviceType + "] Missing values: " + to_string(myTargetInfo->Data().MissingCount()) +
	                      "/" + to_string(myTargetInfo->Data().Size()));
}
<commit_msg>code review changes<commit_after>#include "frost.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "plugin_factory.h"
#include "fetcher.h"
#include <math.h>
#include <NFmiLocation.h>
#include <NFmiMetTime.h>

using namespace std;
using namespace himan;
using namespace himan::plugin;

// rampDown function returns a value between 1 and 0,
// depending where valueInBetween is in the interval between start and end.

double rampDown(const double& start, const double& end, const double& valueInBetween)
{
	if (valueInBetween <= start)
		return 1.0;

	if (valueInBetween >= end)
		return 0.0;

	return fabs((valueInBetween - end) / (end - start));
}

const param FParam("PROB-FROST");

frost::frost()
{
	itsLogger = logger("frost");
}

void frost::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({FParam});

	Start();
}

void frost::Calculate(shared_ptr<info<double>> myTargetInfo, unsigned short threadIndex)
{
	NFmiMetTime theTime(static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%Y"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%m"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%d"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%H"))),
	                    static_cast<short>(stoi(myTargetInfo->Time().ValidDateTime().String("%M"))));

	const param TParam("T-K");
	const param TDParam("TD-K");
	const param TGParam("TG-K");
	const param WGParam("FFG-MS");
	const param T0Param("PROB-TC-0");
	const param NParam("N-0TO1");
	const param RADParam("RADGLO-WM2");
	//const param LCParam("LC-0TO1");
	//const param ICNParam("ICNCT-PRCNT");

	auto myThreadedLogger = logger("frostThread #" + to_string(threadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + ", level " +
			static_cast<string>(forecastLevel));

	auto cnf = make_shared<plugin_configuration>(*itsConfiguration);
	auto f = GET_PLUGIN(fetcher);

	cnf->SourceProducers({producer(131, 98, 150, "ECG")});

        const std::vector<std::string>& names = { "ECEUR0100", "ECGLO0100", "ECEUR0200" };
	cnf->SourceGeomNames(names);

	info_t TInfo = Fetch(forecastTime, level(kGround, 0), TParam, forecastType, false);
	info_t TDInfo = Fetch(forecastTime, level(kGround, 0), TDParam, forecastType, false);
	info_t TGInfo = Fetch(forecastTime, level(kGroundDepth, 0, 7), TGParam, forecastType, false);
	info_t WGInfo = Fetch(forecastTime, level(kGround, 0), WGParam, forecastType, false);
	info_t NInfo = Fetch(forecastTime, level(kGround, 0), NParam, forecastType, false);
        //info_t LCInfo = Fetch(forecastTime, level(kGround, 0), LCParam, forecastType, false);

        info_t RADInfo;

        try
        {
                cnf->SourceProducers({producer(240, 86, 240, "ECGMTA")});
                RADInfo = f->Fetch(cnf, forecastTime, level(kHeight, 0), RADParam, forecastType, false);
        }
        catch (HPExceptionType& e)
        {
                if  (e == kFileDataNotFound)
                {
                        myThreadedLogger.Error("No data found.");
                }
                return;
        }

	info_t T0ECInfo;

	forecast_type stat_type = forecast_type(kStatisticalProcessing);

	try
	{
		cnf->SourceProducers({producer(242, 86, 242, "ECM_PROB")});
		cnf->SourceGeomNames({"ECEUR0200"});
		T0ECInfo = f->Fetch(cnf, forecastTime, level(kGround, 0), T0Param, stat_type, false);
	}
	catch (HPExceptionType& e)
	{
		if  (e == kFileDataNotFound)
		{
			myThreadedLogger.Error("No data found.");
		}
		return;
	}

	info_t T0MEPSInfo;

	try
	{
		cnf->SourceProducers({producer(260, 86, 204, "MEPSMTA")});
		cnf->SourceGeomNames({"MEPS2500D"});
		T0MEPSInfo = f->Fetch(cnf, forecastTime, level(kHeight, 2), T0Param, stat_type, false);
	}
	catch (HPExceptionType& e)
	{
                if  (e == kFileDataNotFound)
                {
                        myThreadedLogger.Error("No data found.");
                }
                //return;
	}

	/*info_t ICNInfo;

	try
	{
		cnf->SourceProducers({producer(150, 86, 150, "HBM_EC")});
		cnf->SourceGeomNames({"HBM"});
		ICNInfo = f->Fetch(cnf, forecastTime, level(kHeight, 0), ICNParam, forecastType, false);
	}
	        catch (HPExceptionType& e)
        {
                if  (e == kFileDataNotFound)
                {
                        myThreadedLogger.Error("No data found.");
                }
                return;
	}*/

	if (!TInfo || !TDInfo || !TGInfo || !WGInfo || !NInfo || !RADInfo || !T0ECInfo || !T0MEPSInfo) // LCINfo, ICNInfo removed.
	{
		myThreadedLogger.Warning("Skipping step " + static_cast<string>(forecastTime.Step()) + ", level " +
			static_cast<string>(forecastLevel));
		return;
	}

	string deviceType = "CPU";

	LOCKSTEP(myTargetInfo, TInfo, TDInfo, TGInfo, WGInfo, NInfo, RADInfo, T0ECInfo, T0MEPSInfo) // LCInfo, ICNInfo, removed.

	{
		double T = TInfo->Value() - himan::constants::kKelvin;
		double TD = TDInfo->Value() - himan::constants::kKelvin;
		double TG = TGInfo->Value() - himan::constants::kKelvin;
		double WG = WGInfo->Value();
		double N = NInfo->Value();
		double RAD = RADInfo->Value();
		double T0EC = T0ECInfo->Value();
		double T0MEPS = T0MEPSInfo->Value();
		//double LC = TInfo->Value();
		//double ICN = TInfo->Value();

		if (IsMissingValue({T, TD, TG, WG, N, RAD, T0EC, T0MEPS})) // LC, ICN
		{
			continue;
		}

		// Calculating indexes and coefficients.

		// dewIndex

		double dewIndex = kHPMissingValue;

		dewIndex = rampDown(-5, 5, TD); // TD -5...5

		// nIndex

		double nIndex = kHPMissingValue;

		nIndex = 1.0 - N;

		// tIndexHigh

		double tIndexHigh = kHPMissingValue;

		tIndexHigh = rampDown(2.5, 15, T) * rampDown(2.5, 15, T);

		// wgWind

		double wgWind = kHPMissingValue;

		wgWind = rampDown(1, 6, WG);

		double lowWindCoef = 1.0;
		double nCoef = 1.0;
		double weight = 4.0;
		double stabCoef = 1.5 * wgWind + 1.0;

		// Adjusting coefficients when T above and near zero.

		if (T >= 0 && T < 2.5)
		{
			lowWindCoef = rampDown(0, 2.5, T) * weight + 1.0;
			nCoef = 1.0 / lowWindCoef;
		}

		// Calculating frost probability.

		double frost_prob = kHPMissingValue;

		if (T < -3.0)
		{
			frost_prob = 1.0;
		}

		else if (T < 0)
		{
			frost_prob = 0.9;
		}

		else
		{
			frost_prob = ((lowWindCoef * dewIndex) + stabCoef + (nCoef * nIndex)) /
				(lowWindCoef + stabCoef + (1.0 / lowWindCoef)) * tIndexHigh;
		}

		// Raising the frost probability due to ground temperature TG.

		double tgModel = kHPMissingValue;

		if (T < 5)
		{
			tgModel = sqrt(rampDown(-6, 5, TG));
		}

		if (frost_prob < tgModel)
		{
			frost_prob = tgModel;
		}

		// Raising the frost probability due to probability of T<0 and T. Both EC and MEPS cases.

		if (T0EC > 0.6 && T < 5.0 && frost_prob < T0EC)
		{
			frost_prob = T0EC;
		}

		if (frost_prob < T0MEPS && T0MEPS > 0.4)
		{
			frost_prob = (frost_prob * 2.0 + T0MEPS) / 3.0;
		}

		// No frost when radiation is high enough. Valid in  1.4.-15.9.

		int month = stoi(forecastTime.ValidDateTime().String("%m"));
		int day = stoi(forecastTime.ValidDateTime().String("%d"));

		if ((month >= 4 && month <= 8) || (month == 9 && day <= 15))
		{
			if (RAD > 175)
			{
				frost_prob = 0.0;
			}

		}

		// Lowering frost probability due to sun's elevation angle. Valid in 1.4.-15.9.

		if ((month >= 4 && month <= 8) || (month == 9 && day <= 15))
		{
			NFmiLocation theLocation(myTargetInfo->LatLon().X(), myTargetInfo->LatLon().Y());

                	double elevationAngle = theLocation.ElevationAngle(theTime);
                	double angleCoef = kHPMissingValue;

			angleCoef = rampDown(-1, 20, elevationAngle);

			frost_prob = angleCoef * frost_prob;
		}

		// No frost probability on sea when there is no ice.

		/*if (ICN == 0 &&  LC == 0)
		{
			frost_prob = 0;
		}*/

		// Lowering frost probability when forecasted T is high enough.

		if (T > 6.0)
		{
			frost_prob = frost_prob * rampDown(6, 15, T);
		}

		myTargetInfo->Value(frost_prob);

	}

	myThreadedLogger.Info("[" + deviceType + "] Missing values: " + to_string(myTargetInfo->Data().MissingCount()) +
	                      "/" + to_string(myTargetInfo->Data().Size()));
}
<|endoftext|>


<commit_before>/**
 * @file icing.cpp
 *
 */

#include "icing.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "plugin_factory.h"

#include "hitool.h"

using namespace std;
using namespace himan::plugin;

icing::icing()
{
	itsLogger = logger("icing");
}

void icing::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({param("ICING-N", 480, 0, 19, 7)});

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void icing::Calculate(shared_ptr<info> myTargetInfo, unsigned short theThreadIndex)
{
	// Required source parameters

	const param TParam("T-K");
	const params VvParam = {param("VV-MS"), param("VV-MMS")};
	const param ClParam("CLDWAT-KGKG");
	const param PrecFormParam("PRECFORM-N");
        const param PrecParam("RR-1-MM");
        const param ZeroLevelParam("H0C-M");
        const param HeightParam("HL-M"); // Height of the current hybrid level

	const level surface(himan::kHeight, 0, "HEIGHT");

	auto myThreadedLogger = logger("icingThread #" + to_string(theThreadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                      static_cast<string>(forecastLevel));

	info_t TInfo = Fetch(forecastTime, forecastLevel, TParam, forecastType, false);
	info_t VvInfo = Fetch(forecastTime, forecastLevel, VvParam, forecastType, false);
	info_t ClInfo = Fetch(forecastTime, forecastLevel, ClParam, forecastType, false);
	info_t PrecFormInfo = Fetch(forecastTime, surface, PrecFormParam, forecastType, false);  // fetch from surface
        info_t PrecInfo = Fetch(forecastTime, surface, PrecParam, forecastType, false);
        info_t ZeroLevelInfo = Fetch(forecastTime, surface, ZeroLevelParam, forecastType, false);
        info_t HeightInfo = Fetch(forecastTime, forecastLevel, HeightParam, forecastType, false);

	if (!TInfo || !VvInfo || !ClInfo)
	{
		myThreadedLogger.Warning("Skipping step " + to_string(forecastTime.Step()) + ", level " +
		                         static_cast<string>(forecastLevel));
		return;
	}

	double VvScale = 1;  // Assume we'll have VV-MMS
	double ClScale = 1000;

	if (VvInfo->Param().Name() == "VV-MS")
	{
		VvScale = 1000;
	}

	ASSERT(TInfo->Grid()->AB() == VvInfo->Grid()->AB() && TInfo->Grid()->AB() == ClInfo->Grid()->AB());

	SetAB(myTargetInfo, TInfo);

	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));
        h->Configuration(itsConfiguration);
        h->Time(myTargetInfo->Time());
        // Stratus cloud base [m] (0-300m=0-985ft, N>50%
	auto base = h->VerticalHeightGreaterThan(NParam, 0, 300, 50);

	string deviceType = "CPU";

	auto& target = VEC(myTargetInfo);

	// LOCKSTEP(myTargetInfo, TInfo, VvInfo, ClInfo)
	for (auto&& tup : zip_range(target, VEC(TInfo), VEC(VvInfo), VEC(ClInfo), VEC(PrecFormInfo), VEC(PrecInfo),
					VEC(ZeroLevelInfo), VEC(HeightInfo), base))
	{
		double& result = tup.get<0>();
		double T = tup.get<1>();
		double Vv = tup.get<2>();
		double Cl = tup.get<3>();
		double Pf = tup.get<4>();
                double Rr = tup.get<5>();
                double Zl = tup.get<6>();
                double Hl = tup.get<7>();
                double StrBase = tup.get<8>();

		if (IsMissingValue({T, Vv, Cl}))
		{
			continue;
		}

		double Icing;
		double TBase = constants::kKelvin;
		int vCor = kHPMissingInt;
		int tCor = kHPMissingInt;

		T = T - TBase;
		Vv *= VvScale;
		Cl *= ClScale;

		// Vertical velocity correction factor

		if (Vv < 0)
		{
			vCor = -1;
		}
		else if ((Vv >= 0) && (Vv <= 50))
		{
			vCor = 0;
		}
		else if ((Vv >= 50) && (Vv <= 100))
		{
			vCor = 1;
		}
		else if ((Vv >= 100) && (Vv <= 200))
		{
			vCor = 2;
		}
		else if ((Vv >= 200) && (Vv <= 300))
		{
			vCor = 3;
		}
		else if ((Vv >= 300) && (Vv <= 1000))
		{
			vCor = 4;
		}
		else if (Vv > 1000)
		{
			vCor = 5;
		}

		// Temperature correction factor

		if ((T <= 0) && (T > -1))
		{
			tCor = -2;
		}
		else if ((T <= -1) && (T > -2))
		{
			tCor = -1;
		}
		else if ((T <= -2) && (T > -3))
		{
			tCor = 0;
		}
		else if ((T <= -3) && (T > -12))
		{
			tCor = 1;
		}
		else if ((T <= -12) && (T > -15))
		{
			tCor = 0;
		}
		else if ((T <= -15) && (T > -18))
		{
			tCor = -1;
		}
		else if (T < -18)
		{
			tCor = -2;
		}
		else
		{
			tCor = 0;
		}

		if ((Cl <= 0) || (T > 0))
		{
			Icing = 0;
		}
		else
		{
			Icing = round(log(Cl) + 6) + vCor + tCor;
		}

		// freezing drizzle, values applied to all model levels starting from ~base of the stratus cloud
		if (Pf == 4)
                {
                        if (StrBase >= Hl)
                        {
                                Icing = 6 + Rr * 10;
                        }
                }

		// freezing rain, values applied to all model levels in the surface sub-zero layer
		if (Pf == 5)
                {
                        if (Zl >= Hl)
                        {
                                Icing = 7 + Rr * 1.5;
                        }
                }

		// Maximum and minimum values for index

		if (Icing > 15)
		{
			Icing = 15;
		}

		if (Icing < 0)
		{
			Icing = 0;
		}

		result = Icing;
	}

	myThreadedLogger.Info("[" + deviceType + "] Missing values: " + to_string(myTargetInfo->Data().MissingCount()) +
	                      "/" + to_string(myTargetInfo->Data().Size()));
}
<commit_msg>Added missing param<commit_after>/**
 * @file icing.cpp
 *
 */

#include "icing.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "plugin_factory.h"

#include "hitool.h"

using namespace std;
using namespace himan::plugin;

icing::icing()
{
	itsLogger = logger("icing");
}

void icing::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	SetParams({param("ICING-N", 480, 0, 19, 7)});

	Start();
}

/*
 * Calculate()
 *
 * This function does the actual calculation.
 */

void icing::Calculate(shared_ptr<info> myTargetInfo, unsigned short theThreadIndex)
{
	// Required source parameters

	const param TParam("T-K");
	const params VvParam = {param("VV-MS"), param("VV-MMS")};
	const params NParam({himan::param("N-PRCNT"), himan::param("N-0TO1")});
	const param ClParam("CLDWAT-KGKG");
	const param PrecFormParam("PRECFORM-N");
        const param PrecParam("RR-1-MM");
        const param ZeroLevelParam("H0C-M");
        const param HeightParam("HL-M"); // Height of the current hybrid level

	const level surface(himan::kHeight, 0, "HEIGHT");

	auto myThreadedLogger = logger("icingThread #" + to_string(theThreadIndex));

	forecast_time forecastTime = myTargetInfo->Time();
	level forecastLevel = myTargetInfo->Level();
	forecast_type forecastType = myTargetInfo->ForecastType();

	myThreadedLogger.Info("Calculating time " + static_cast<string>(forecastTime.ValidDateTime()) + " level " +
	                      static_cast<string>(forecastLevel));

	info_t TInfo = Fetch(forecastTime, forecastLevel, TParam, forecastType, false);
	info_t VvInfo = Fetch(forecastTime, forecastLevel, VvParam, forecastType, false);
	info_t ClInfo = Fetch(forecastTime, forecastLevel, ClParam, forecastType, false);
	info_t PrecFormInfo = Fetch(forecastTime, surface, PrecFormParam, forecastType, false);  // fetch from surface
        info_t PrecInfo = Fetch(forecastTime, surface, PrecParam, forecastType, false);
        info_t ZeroLevelInfo = Fetch(forecastTime, surface, ZeroLevelParam, forecastType, false);
        info_t HeightInfo = Fetch(forecastTime, forecastLevel, HeightParam, forecastType, false);

	if (!TInfo || !VvInfo || !ClInfo)
	{
		myThreadedLogger.Warning("Skipping step " + to_string(forecastTime.Step()) + ", level " +
		                         static_cast<string>(forecastLevel));
		return;
	}

	double VvScale = 1;  // Assume we'll have VV-MMS
	double ClScale = 1000;

	if (VvInfo->Param().Name() == "VV-MS")
	{
		VvScale = 1000;
	}

	ASSERT(TInfo->Grid()->AB() == VvInfo->Grid()->AB() && TInfo->Grid()->AB() == ClInfo->Grid()->AB());

	SetAB(myTargetInfo, TInfo);

	auto h = dynamic_pointer_cast<hitool>(plugin_factory::Instance()->Plugin("hitool"));
        h->Configuration(itsConfiguration);
        h->Time(myTargetInfo->Time());
        // Stratus cloud base [m] (0-300m=0-985ft, N>50%
	auto base = h->VerticalHeightGreaterThan(NParam, 0, 300, 50);

	string deviceType = "CPU";

	auto& target = VEC(myTargetInfo);

	// LOCKSTEP(myTargetInfo, TInfo, VvInfo, ClInfo)
	for (auto&& tup : zip_range(target, VEC(TInfo), VEC(VvInfo), VEC(ClInfo), VEC(PrecFormInfo), VEC(PrecInfo),
					VEC(ZeroLevelInfo), VEC(HeightInfo), base))
	{
		double& result = tup.get<0>();
		double T = tup.get<1>();
		double Vv = tup.get<2>();
		double Cl = tup.get<3>();
		double Pf = tup.get<4>();
                double Rr = tup.get<5>();
                double Zl = tup.get<6>();
                double Hl = tup.get<7>();
                double StrBase = tup.get<8>();

		if (IsMissingValue({T, Vv, Cl}))
		{
			continue;
		}

		double Icing;
		double TBase = constants::kKelvin;
		int vCor = kHPMissingInt;
		int tCor = kHPMissingInt;

		T = T - TBase;
		Vv *= VvScale;
		Cl *= ClScale;

		// Vertical velocity correction factor

		if (Vv < 0)
		{
			vCor = -1;
		}
		else if ((Vv >= 0) && (Vv <= 50))
		{
			vCor = 0;
		}
		else if ((Vv >= 50) && (Vv <= 100))
		{
			vCor = 1;
		}
		else if ((Vv >= 100) && (Vv <= 200))
		{
			vCor = 2;
		}
		else if ((Vv >= 200) && (Vv <= 300))
		{
			vCor = 3;
		}
		else if ((Vv >= 300) && (Vv <= 1000))
		{
			vCor = 4;
		}
		else if (Vv > 1000)
		{
			vCor = 5;
		}

		// Temperature correction factor

		if ((T <= 0) && (T > -1))
		{
			tCor = -2;
		}
		else if ((T <= -1) && (T > -2))
		{
			tCor = -1;
		}
		else if ((T <= -2) && (T > -3))
		{
			tCor = 0;
		}
		else if ((T <= -3) && (T > -12))
		{
			tCor = 1;
		}
		else if ((T <= -12) && (T > -15))
		{
			tCor = 0;
		}
		else if ((T <= -15) && (T > -18))
		{
			tCor = -1;
		}
		else if (T < -18)
		{
			tCor = -2;
		}
		else
		{
			tCor = 0;
		}

		if ((Cl <= 0) || (T > 0))
		{
			Icing = 0;
		}
		else
		{
			Icing = round(log(Cl) + 6) + vCor + tCor;
		}

		// freezing drizzle, values applied to all model levels starting from ~base of the stratus cloud
		if (Pf == 4)
                {
                        if (StrBase >= Hl)
                        {
                                Icing = 6 + Rr * 10;
                        }
                }

		// freezing rain, values applied to all model levels in the surface sub-zero layer
		if (Pf == 5)
                {
                        if (Zl >= Hl)
                        {
                                Icing = 7 + Rr * 1.5;
                        }
                }

		// Maximum and minimum values for index

		if (Icing > 15)
		{
			Icing = 15;
		}

		if (Icing < 0)
		{
			Icing = 0;
		}

		result = Icing;
	}

	myThreadedLogger.Info("[" + deviceType + "] Missing values: " + to_string(myTargetInfo->Data().MissingCount()) +
	                      "/" + to_string(myTargetInfo->Data().Size()));
}
<|endoftext|>


<commit_before>/*	$OpenBSD: bcrypt.c,v 1.31 2014/03/22 23:02:03 tedu Exp $	*/

/*
 * Copyright (c) 1997 Niels Provos <provos@umich.edu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* This password hashing algorithm was designed by David Mazieres
 * <dm@lcs.mit.edu> and works as follows:
 *
 * 1. state := InitState ()
 * 2. state := ExpandKey (state, salt, password)
 * 3. REPEAT rounds:
 *    	state := ExpandKey (state, 0, password)
 *    state := ExpandKey (state, 0, salt)
 * 4. ctext := "OrpheanBeholderScryDoubt"
 * 5. REPEAT 64:
 *    	ctext := Encrypt_ECB (state, ctext);
 * 6. RETURN Concatenate (salt, ctext);
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>

#include "node_blf.h"

#ifdef _WIN32
#define snprintf _snprintf
#endif

//#if !defined(__APPLE__) && !defined(__MACH__)
//#include "bsd/stdlib.h"
//#endif

/* This implementation is adaptable to current computing power.
 * You can have up to 2^31 rounds which should be enough for some
 * time to come.
 */

static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t);
static void decode_base64(u_int8_t *, u_int16_t, u_int8_t *);

const static char* error = ":";

const static u_int8_t Base64Code[] =
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

const static u_int8_t index_64[128] = {
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
	56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
	255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
	255, 255, 255, 255, 255, 255, 28, 29, 30,
	31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
	41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
	51, 52, 53, 255, 255, 255, 255, 255
};
#define CHAR64(c)  ( (c) > 127 ? 255 : index_64[(c)])

static void
decode_base64(u_int8_t *buffer, u_int16_t len, u_int8_t *data)
{
	u_int8_t *bp = buffer;
	u_int8_t *p = data;
	u_int8_t c1, c2, c3, c4;
	while (bp < buffer + len) {
		c1 = CHAR64(*p);
		c2 = CHAR64(*(p + 1));

		/* Invalid data */
		if (c1 == 255 || c2 == 255)
			break;

		*bp++ = (c1 << 2) | ((c2 & 0x30) >> 4);
		if (bp >= buffer + len)
			break;

		c3 = CHAR64(*(p + 2));
		if (c3 == 255)
			break;

		*bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
		if (bp >= buffer + len)
			break;

		c4 = CHAR64(*(p + 3));
		if (c4 == 255)
			break;
		*bp++ = ((c3 & 0x03) << 6) | c4;

		p += 4;
	}
}

void
encode_salt(char *salt, u_int8_t *csalt, char minor, u_int16_t clen, u_int8_t logr)
{
	salt[0] = '$';
	salt[1] = BCRYPT_VERSION;
	salt[2] = minor;
	salt[3] = '$';

    // Max rounds are 31
	snprintf(salt + 4, 4, "%2.2u$", logr & 0x001F);

	encode_base64((u_int8_t *) salt + 7, csalt, clen);
}

/* Generates a salt for this version of crypt.
   Since versions may change. Keeping this here
   seems sensible.
   from: http://mail-index.netbsd.org/tech-crypto/2002/05/24/msg000204.html
*/
void
bcrypt_gensalt(char minor, u_int8_t log_rounds, u_int8_t *seed, char *gsalt)
{
	if (log_rounds < 4)
		log_rounds = 4;
	else if (log_rounds > 31)
		log_rounds = 31;

	encode_salt(gsalt, seed, minor, BCRYPT_MAXSALT, log_rounds);
}

/* We handle $Vers$log2(NumRounds)$salt+passwd$
   i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */

void
bcrypt(const char *key, size_t key_len, const char *salt, char *encrypted)
{
	blf_ctx state;
	u_int32_t rounds, i, k;
	u_int16_t j;
	u_int8_t salt_len, logr, minor;
	u_int8_t ciphertext[4 * BCRYPT_BLOCKS+1] = "OrpheanBeholderScryDoubt";
	u_int8_t csalt[BCRYPT_MAXSALT];
	u_int32_t cdata[BCRYPT_BLOCKS];
	int n;

	/* Discard "$" identifier */
	salt++;

	if (*salt > BCRYPT_VERSION) {
		/* How do I handle errors ? Return ':' */
		strcpy(encrypted, error);
		return;
	}

	/* Check for minor versions */
	if (salt[1] != '$') {
		 switch (salt[1]) {
		 case 'a': /* 'ab' should not yield the same as 'abab' */
		 case 'b': /* cap input length at 72 bytes */
			 minor = salt[1];
			 salt++;
			 break;
		 default:
			 strcpy(encrypted, error);
			 return;
		 }
	} else
		 minor = 0;

	/* Discard version + "$" identifier */
	salt += 2;

	if (salt[2] != '$') {
		/* Out of sync with passwd entry */
		strcpy(encrypted, error);
		return;
	}

	/* Computer power doesn't increase linear, 2^x should be fine */
	n = atoi(salt);
	if (n > 31 || n < 0) {
		strcpy(encrypted, error);
		return;
	}
	logr = (u_int8_t)n;
	if ((rounds = (u_int32_t) 1 << logr) < BCRYPT_MINROUNDS) {
		strcpy(encrypted, error);
		return;
	}

	/* Discard num rounds + "$" identifier */
	salt += 3;

	if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
		strcpy(encrypted, error);
		return;
	}

	/* We dont want the base64 salt but the raw data */
	decode_base64(csalt, BCRYPT_MAXSALT, (u_int8_t *) salt);
	salt_len = BCRYPT_MAXSALT;
	if (minor <= 'a')
		key_len = (u_int8_t)(key_len + (minor >= 'a' ? 1 : 0));
	else
	{
		/* size_t, but the function calls
		* below result in implicit casts to a narrower integer
		* type, so cap key_len at the actual maximum supported
		* length here to avoid integer wraparound */
		if (key_len > 72)
			key_len = 72;
		key_len++; /* include the NUL */
	}

	/* Setting up S-Boxes and Subkeys */
	Blowfish_initstate(&state);
	Blowfish_expandstate(&state, csalt, salt_len,
		(u_int8_t *) key, key_len);
	for (k = 0; k < rounds; k++) {
		Blowfish_expand0state(&state, (u_int8_t *) key, key_len);
		Blowfish_expand0state(&state, csalt, salt_len);
	}

 	/* This can be precomputed later */
	j = 0;
	for (i = 0; i < BCRYPT_BLOCKS; i++)
		cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_BLOCKS, &j);

	/* Now do the encryption */
	for (k = 0; k < 64; k++)
		blf_enc(&state, cdata, BCRYPT_BLOCKS / 2);

	for (i = 0; i < BCRYPT_BLOCKS; i++) {
		ciphertext[4 * i + 3] = cdata[i] & 0xff;
		cdata[i] = cdata[i] >> 8;
		ciphertext[4 * i + 2] = cdata[i] & 0xff;
		cdata[i] = cdata[i] >> 8;
		ciphertext[4 * i + 1] = cdata[i] & 0xff;
		cdata[i] = cdata[i] >> 8;
		ciphertext[4 * i + 0] = cdata[i] & 0xff;
	}

	i = 0;
	encrypted[i++] = '$';
	encrypted[i++] = BCRYPT_VERSION;
	if (minor)
		encrypted[i++] = minor;
	encrypted[i++] = '$';

	snprintf(encrypted + i, 4, "%2.2u$", logr & 0x001F);

	encode_base64((u_int8_t *) encrypted + i + 3, csalt, BCRYPT_MAXSALT);
	encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext,
		4 * BCRYPT_BLOCKS - 1);
	memset(&state, 0, sizeof(state));
	memset(ciphertext, 0, sizeof(ciphertext));
	memset(csalt, 0, sizeof(csalt));
	memset(cdata, 0, sizeof(cdata));
}

u_int32_t bcrypt_get_rounds(const char * hash)
{
  /* skip past the leading "$" */
  if (!hash || *(hash++) != '$') return 0;

  /* skip past version */
  if (0 == (*hash++)) return 0;
  if (*hash && *hash != '$') hash++;
  if (*hash++ != '$') return 0;

  return  atoi(hash);
}

static void
encode_base64(u_int8_t *buffer, u_int8_t *data, u_int16_t len)
{
	u_int8_t *bp = buffer;
	u_int8_t *p = data;
	u_int8_t c1, c2;
	while (p < data + len) {
		c1 = *p++;
		*bp++ = Base64Code[(c1 >> 2)];
		c1 = (c1 & 0x03) << 4;
		if (p >= data + len) {
			*bp++ = Base64Code[c1];
			break;
		}
		c2 = *p++;
		c1 |= (c2 >> 4) & 0x0f;
		*bp++ = Base64Code[c1];
		c1 = (c2 & 0x0f) << 2;
		if (p >= data + len) {
			*bp++ = Base64Code[c1];
			break;
		}
		c2 = *p++;
		c1 |= (c2 >> 6) & 0x03;
		*bp++ = Base64Code[c1];
		*bp++ = Base64Code[c2 & 0x3f];
	}
	*bp = '\0';
}
<commit_msg>Reword comment<commit_after>/*	$OpenBSD: bcrypt.c,v 1.31 2014/03/22 23:02:03 tedu Exp $	*/

/*
 * Copyright (c) 1997 Niels Provos <provos@umich.edu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* This password hashing algorithm was designed by David Mazieres
 * <dm@lcs.mit.edu> and works as follows:
 *
 * 1. state := InitState ()
 * 2. state := ExpandKey (state, salt, password)
 * 3. REPEAT rounds:
 *    	state := ExpandKey (state, 0, password)
 *    state := ExpandKey (state, 0, salt)
 * 4. ctext := "OrpheanBeholderScryDoubt"
 * 5. REPEAT 64:
 *    	ctext := Encrypt_ECB (state, ctext);
 * 6. RETURN Concatenate (salt, ctext);
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>

#include "node_blf.h"

#ifdef _WIN32
#define snprintf _snprintf
#endif

//#if !defined(__APPLE__) && !defined(__MACH__)
//#include "bsd/stdlib.h"
//#endif

/* This implementation is adaptable to current computing power.
 * You can have up to 2^31 rounds which should be enough for some
 * time to come.
 */

static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t);
static void decode_base64(u_int8_t *, u_int16_t, u_int8_t *);

const static char* error = ":";

const static u_int8_t Base64Code[] =
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

const static u_int8_t index_64[128] = {
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
	56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
	255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
	7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
	255, 255, 255, 255, 255, 255, 28, 29, 30,
	31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
	41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
	51, 52, 53, 255, 255, 255, 255, 255
};
#define CHAR64(c)  ( (c) > 127 ? 255 : index_64[(c)])

static void
decode_base64(u_int8_t *buffer, u_int16_t len, u_int8_t *data)
{
	u_int8_t *bp = buffer;
	u_int8_t *p = data;
	u_int8_t c1, c2, c3, c4;
	while (bp < buffer + len) {
		c1 = CHAR64(*p);
		c2 = CHAR64(*(p + 1));

		/* Invalid data */
		if (c1 == 255 || c2 == 255)
			break;

		*bp++ = (c1 << 2) | ((c2 & 0x30) >> 4);
		if (bp >= buffer + len)
			break;

		c3 = CHAR64(*(p + 2));
		if (c3 == 255)
			break;

		*bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
		if (bp >= buffer + len)
			break;

		c4 = CHAR64(*(p + 3));
		if (c4 == 255)
			break;
		*bp++ = ((c3 & 0x03) << 6) | c4;

		p += 4;
	}
}

void
encode_salt(char *salt, u_int8_t *csalt, char minor, u_int16_t clen, u_int8_t logr)
{
	salt[0] = '$';
	salt[1] = BCRYPT_VERSION;
	salt[2] = minor;
	salt[3] = '$';

    // Max rounds are 31
	snprintf(salt + 4, 4, "%2.2u$", logr & 0x001F);

	encode_base64((u_int8_t *) salt + 7, csalt, clen);
}

/* Generates a salt for this version of crypt.
   Since versions may change. Keeping this here
   seems sensible.
   from: http://mail-index.netbsd.org/tech-crypto/2002/05/24/msg000204.html
*/
void
bcrypt_gensalt(char minor, u_int8_t log_rounds, u_int8_t *seed, char *gsalt)
{
	if (log_rounds < 4)
		log_rounds = 4;
	else if (log_rounds > 31)
		log_rounds = 31;

	encode_salt(gsalt, seed, minor, BCRYPT_MAXSALT, log_rounds);
}

/* We handle $Vers$log2(NumRounds)$salt+passwd$
   i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */

void
bcrypt(const char *key, size_t key_len, const char *salt, char *encrypted)
{
	blf_ctx state;
	u_int32_t rounds, i, k;
	u_int16_t j;
	u_int8_t salt_len, logr, minor;
	u_int8_t ciphertext[4 * BCRYPT_BLOCKS+1] = "OrpheanBeholderScryDoubt";
	u_int8_t csalt[BCRYPT_MAXSALT];
	u_int32_t cdata[BCRYPT_BLOCKS];
	int n;

	/* Discard "$" identifier */
	salt++;

	if (*salt > BCRYPT_VERSION) {
		/* How do I handle errors ? Return ':' */
		strcpy(encrypted, error);
		return;
	}

	/* Check for minor versions */
	if (salt[1] != '$') {
		 switch (salt[1]) {
		 case 'a': /* 'ab' should not yield the same as 'abab' */
		 case 'b': /* cap input length at 72 bytes */
			 minor = salt[1];
			 salt++;
			 break;
		 default:
			 strcpy(encrypted, error);
			 return;
		 }
	} else
		 minor = 0;

	/* Discard version + "$" identifier */
	salt += 2;

	if (salt[2] != '$') {
		/* Out of sync with passwd entry */
		strcpy(encrypted, error);
		return;
	}

	/* Computer power doesn't increase linear, 2^x should be fine */
	n = atoi(salt);
	if (n > 31 || n < 0) {
		strcpy(encrypted, error);
		return;
	}
	logr = (u_int8_t)n;
	if ((rounds = (u_int32_t) 1 << logr) < BCRYPT_MINROUNDS) {
		strcpy(encrypted, error);
		return;
	}

	/* Discard num rounds + "$" identifier */
	salt += 3;

	if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) {
		strcpy(encrypted, error);
		return;
	}

	/* We dont want the base64 salt but the raw data */
	decode_base64(csalt, BCRYPT_MAXSALT, (u_int8_t *) salt);
	salt_len = BCRYPT_MAXSALT;
	if (minor <= 'a')
		key_len = (u_int8_t)(key_len + (minor >= 'a' ? 1 : 0));
	else
	{
		/* cap key_len at the actual maximum supported
		* length here to avoid integer wraparound */
		if (key_len > 72)
			key_len = 72;
		key_len++; /* include the NUL */
	}

	/* Setting up S-Boxes and Subkeys */
	Blowfish_initstate(&state);
	Blowfish_expandstate(&state, csalt, salt_len,
		(u_int8_t *) key, key_len);
	for (k = 0; k < rounds; k++) {
		Blowfish_expand0state(&state, (u_int8_t *) key, key_len);
		Blowfish_expand0state(&state, csalt, salt_len);
	}

 	/* This can be precomputed later */
	j = 0;
	for (i = 0; i < BCRYPT_BLOCKS; i++)
		cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_BLOCKS, &j);

	/* Now do the encryption */
	for (k = 0; k < 64; k++)
		blf_enc(&state, cdata, BCRYPT_BLOCKS / 2);

	for (i = 0; i < BCRYPT_BLOCKS; i++) {
		ciphertext[4 * i + 3] = cdata[i] & 0xff;
		cdata[i] = cdata[i] >> 8;
		ciphertext[4 * i + 2] = cdata[i] & 0xff;
		cdata[i] = cdata[i] >> 8;
		ciphertext[4 * i + 1] = cdata[i] & 0xff;
		cdata[i] = cdata[i] >> 8;
		ciphertext[4 * i + 0] = cdata[i] & 0xff;
	}

	i = 0;
	encrypted[i++] = '$';
	encrypted[i++] = BCRYPT_VERSION;
	if (minor)
		encrypted[i++] = minor;
	encrypted[i++] = '$';

	snprintf(encrypted + i, 4, "%2.2u$", logr & 0x001F);

	encode_base64((u_int8_t *) encrypted + i + 3, csalt, BCRYPT_MAXSALT);
	encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext,
		4 * BCRYPT_BLOCKS - 1);
	memset(&state, 0, sizeof(state));
	memset(ciphertext, 0, sizeof(ciphertext));
	memset(csalt, 0, sizeof(csalt));
	memset(cdata, 0, sizeof(cdata));
}

u_int32_t bcrypt_get_rounds(const char * hash)
{
  /* skip past the leading "$" */
  if (!hash || *(hash++) != '$') return 0;

  /* skip past version */
  if (0 == (*hash++)) return 0;
  if (*hash && *hash != '$') hash++;
  if (*hash++ != '$') return 0;

  return  atoi(hash);
}

static void
encode_base64(u_int8_t *buffer, u_int8_t *data, u_int16_t len)
{
	u_int8_t *bp = buffer;
	u_int8_t *p = data;
	u_int8_t c1, c2;
	while (p < data + len) {
		c1 = *p++;
		*bp++ = Base64Code[(c1 >> 2)];
		c1 = (c1 & 0x03) << 4;
		if (p >= data + len) {
			*bp++ = Base64Code[c1];
			break;
		}
		c2 = *p++;
		c1 |= (c2 >> 4) & 0x0f;
		*bp++ = Base64Code[c1];
		c1 = (c2 & 0x0f) << 2;
		if (p >= data + len) {
			*bp++ = Base64Code[c1];
			break;
		}
		c2 = *p++;
		c1 |= (c2 >> 6) & 0x03;
		*bp++ = Base64Code[c1];
		*bp++ = Base64Code[c2 & 0x3f];
	}
	*bp = '\0';
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2014 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bloom.h"

#include "hash.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "script/standard.h"
#include "streams.h"

#include <math.h>
#include <stdlib.h>

#include <boost/foreach.hpp>

#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552

using namespace std;

CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) : /**
 * The ideal size for a bloom filter with a given number of elements and false positive rate is:
 * - nElements * log(fp rate) / ln(2)^2
 * We ignore filter parameters which will create a bloom filter larger than the protocol limits
 */
                                                                                vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
                                                                                /**
 * The ideal number of hash functions is filter size * ln(2) / number of elements
 * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
 * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
 */
                                                                                isFull(false),
                                                                                isEmpty(false),
                                                                                nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
                                                                                nTweak(nTweakIn),
                                                                                nFlags(nFlagsIn)
{
}

inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
{
    // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}

void CBloomFilter::insert(const vector<unsigned char>& vKey)
{
    if (isFull)
        return;
    for (unsigned int i = 0; i < nHashFuncs; i++) {
        unsigned int nIndex = Hash(i, vKey);
        // Sets bit nIndex of vData
        vData[nIndex >> 3] |= (1 << (7 & nIndex));
    }
    isEmpty = false;
}

void CBloomFilter::insert(const COutPoint& outpoint)
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    insert(data);
}

void CBloomFilter::insert(const uint256& hash)
{
    vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool CBloomFilter::contains(const vector<unsigned char>& vKey) const
{
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    for (unsigned int i = 0; i < nHashFuncs; i++) {
        unsigned int nIndex = Hash(i, vKey);
        // Checks bit nIndex of vData
        if (!(vData[nIndex >> 3] & (1 << (7 & nIndex))))
            return false;
    }
    return true;
}

bool CBloomFilter::contains(const COutPoint& outpoint) const
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    return contains(data);
}

bool CBloomFilter::contains(const uint256& hash) const
{
    vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void CBloomFilter::clear()
{
    vData.assign(vData.size(), 0);
    isFull = false;
    isEmpty = true;
}

bool CBloomFilter::IsWithinSizeConstraints() const
{
    return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
}

bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
{
    bool fFound = false;
    // Match if the filter contains the hash of tx
    //  for finding tx when they appear in a block
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    const uint256& hash = tx.GetHash();
    if (contains(hash))
        fFound = true;

    for (unsigned int i = 0; i < tx.vout.size(); i++) {
        const CTxOut& txout = tx.vout[i];
        // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
        // If this matches, also add the specific output that was matched.
        // This means clients don't have to update the filter themselves when a new relevant tx
        // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
        CScript::const_iterator pc = txout.scriptPubKey.begin();
        vector<unsigned char> data;
        while (pc < txout.scriptPubKey.end()) {
            opcodetype opcode;
            if (!txout.scriptPubKey.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data)) {
                fFound = true;
                if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
                    insert(COutPoint(hash, i));
                else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) {
                    txnouttype type;
                    vector<vector<unsigned char> > vSolutions;
                    if (Solver(txout.scriptPubKey, type, vSolutions) &&
                        (type == TX_PUBKEY || type == TX_MULTISIG))
                        insert(COutPoint(hash, i));
                }
                break;
            }
        }
    }

    if (fFound)
        return true;

    BOOST_FOREACH (const CTxIn& txin, tx.vin) {
        // Match if the filter contains an outpoint tx spends
        if (contains(txin.prevout))
            return true;

        // Match if the filter contains any arbitrary script data element in any scriptSig in tx
        CScript::const_iterator pc = txin.scriptSig.begin();
        vector<unsigned char> data;
        while (pc < txin.scriptSig.end()) {
            opcodetype opcode;
            if (!txin.scriptSig.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
                return true;
        }
    }

    return false;
}

void CBloomFilter::UpdateEmptyFull()
{
    bool full = true;
    bool empty = true;
    for (unsigned int i = 0; i < vData.size(); i++) {
        full &= vData[i] == 0xff;
        empty &= vData[i] == 0;
    }
    isFull = full;
    isEmpty = empty;
}
<commit_msg>Cleanup formatting in bloom.cpp<commit_after>// Copyright (c) 2012-2014 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "bloom.h"

#include "hash.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "script/standard.h"
#include "streams.h"

#include <math.h>
#include <stdlib.h>

#include <boost/foreach.hpp>

#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
#define LN2 0.6931471805599453094172321214581765680755001343602552

using namespace std;

CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) :
 /**
 * The ideal size for a bloom filter with a given number of elements and false positive rate is:
 * - nElements * log(fp rate) / ln(2)^2
 * We ignore filter parameters which will create a bloom filter larger than the protocol limits
 */
	vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
 /**
 * The ideal number of hash functions is filter size * ln(2) / number of elements
 * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
 * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
 */
	isFull(false),
	isEmpty(false),
  nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
  nTweak(nTweakIn),
  nFlags(nFlagsIn)
{
}

inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
{
    // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
    return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
}

void CBloomFilter::insert(const vector<unsigned char>& vKey)
{
    if (isFull)
        return;
    for (unsigned int i = 0; i < nHashFuncs; i++) {
        unsigned int nIndex = Hash(i, vKey);
        // Sets bit nIndex of vData
        vData[nIndex >> 3] |= (1 << (7 & nIndex));
    }
    isEmpty = false;
}

void CBloomFilter::insert(const COutPoint& outpoint)
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    insert(data);
}

void CBloomFilter::insert(const uint256& hash)
{
    vector<unsigned char> data(hash.begin(), hash.end());
    insert(data);
}

bool CBloomFilter::contains(const vector<unsigned char>& vKey) const
{
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    for (unsigned int i = 0; i < nHashFuncs; i++) {
        unsigned int nIndex = Hash(i, vKey);
        // Checks bit nIndex of vData
        if (!(vData[nIndex >> 3] & (1 << (7 & nIndex))))
            return false;
    }
    return true;
}

bool CBloomFilter::contains(const COutPoint& outpoint) const
{
    CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
    stream << outpoint;
    vector<unsigned char> data(stream.begin(), stream.end());
    return contains(data);
}

bool CBloomFilter::contains(const uint256& hash) const
{
    vector<unsigned char> data(hash.begin(), hash.end());
    return contains(data);
}

void CBloomFilter::clear()
{
    vData.assign(vData.size(), 0);
    isFull = false;
    isEmpty = true;
}

bool CBloomFilter::IsWithinSizeConstraints() const
{
    return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
}

bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
{
    bool fFound = false;
    // Match if the filter contains the hash of tx
    //  for finding tx when they appear in a block
    if (isFull)
        return true;
    if (isEmpty)
        return false;
    const uint256& hash = tx.GetHash();
    if (contains(hash))
        fFound = true;

    for (unsigned int i = 0; i < tx.vout.size(); i++) {
        const CTxOut& txout = tx.vout[i];
        // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
        // If this matches, also add the specific output that was matched.
        // This means clients don't have to update the filter themselves when a new relevant tx
        // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
        CScript::const_iterator pc = txout.scriptPubKey.begin();
        vector<unsigned char> data;
        while (pc < txout.scriptPubKey.end()) {
            opcodetype opcode;
            if (!txout.scriptPubKey.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data)) {
                fFound = true;
                if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
                    insert(COutPoint(hash, i));
                else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) {
                    txnouttype type;
                    vector<vector<unsigned char> > vSolutions;
                    if (Solver(txout.scriptPubKey, type, vSolutions) &&
                        (type == TX_PUBKEY || type == TX_MULTISIG))
                        insert(COutPoint(hash, i));
                }
                break;
            }
        }
    }

    if (fFound)
        return true;

    BOOST_FOREACH (const CTxIn& txin, tx.vin) {
        // Match if the filter contains an outpoint tx spends
        if (contains(txin.prevout))
            return true;

        // Match if the filter contains any arbitrary script data element in any scriptSig in tx
        CScript::const_iterator pc = txin.scriptSig.begin();
        vector<unsigned char> data;
        while (pc < txin.scriptSig.end()) {
            opcodetype opcode;
            if (!txin.scriptSig.GetOp(pc, opcode, data))
                break;
            if (data.size() != 0 && contains(data))
                return true;
        }
    }

    return false;
}

void CBloomFilter::UpdateEmptyFull()
{
    bool full = true;
    bool empty = true;
    for (unsigned int i = 0; i < vData.size(); i++) {
        full &= vData[i] == 0xff;
        empty &= vData[i] == 0;
    }
    isFull = full;
    isEmpty = empty;
}
<|endoftext|>


<commit_before>#include "box2d.hpp"
#include <exception>
#include <iostream>
#include <stdexcept>

Box2D::Box2D(Box2D && obj)
{
  swap(obj);
}

Box2D & Box2D::operator = (Box2D const & obj)
{
  if (this == &obj) return *this;
  Box2D tmp(obj);
  swap(tmp);
  return *this;
}

Box2D & Box2D::operator = (Box2D && obj)
{
  swap(obj);
  return *this;
}

bool Box2D::operator == (Box2D const & obj) const
{
  return m_boxMin == obj.m_boxMin
      && m_boxMax == obj.m_boxMax;
}

void Box2D::swap(Box2D & obj)
{
  std::swap(m_boxMin, obj.m_boxMin);
  std::swap(m_boxMax, obj.m_boxMax);
}

bool Box2D::checkInside(const Box2D & box, const Point2D & point)
{
  if ((point.x() >= box.m_boxMin.x() &&
       point.x() <= box.m_boxMax.x() &&
       point.y() >= box.m_boxMin.y() &&
       point.y() <= box.m_boxMax.y()) ||
      (point.x() >= box.m_boxMax.x() &&
       point.x() <= box.m_boxMin.x() &&
       point.y() >= box.m_boxMax.y() &&
       point.y() <= box.m_boxMin.y())
      )
  {
    return true;
  }

  return false;
}

bool Box2D::checkBoxes(const Box2D & box1, const Box2D & box2)
{
  // If two boxes are not intersected with each other
  // then return false.
  return not ( box1.boxMax().x() <= box2.boxMin().x() ||
           box2.boxMax().x() <= box1.boxMin().x() ||
           box1.boxMin().y() >= box2.boxMax().y() ||
           box2.boxMin().y() >= box1.boxMax().y() );
}

Box2D Box2D::createBox(const Point2D & minPoint, const Point2D & maxPoint)
{
  if(minPoint.x()>=maxPoint.x() || minPoint.y()>=maxPoint.y())
    throw std::invalid_argument("You must create correct box");
  return Box2D(minPoint,maxPoint);
}
<commit_msg>edit checkInside<commit_after>#include "box2d.hpp"
#include <exception>
#include <iostream>
#include <stdexcept>

Box2D::Box2D(Box2D && obj)
{
  swap(obj);
}

Box2D & Box2D::operator = (Box2D const & obj)
{
  if (this == &obj) return *this;
  Box2D tmp(obj);
  swap(tmp);
  return *this;
}

Box2D & Box2D::operator = (Box2D && obj)
{
  swap(obj);
  return *this;
}

bool Box2D::operator == (Box2D const & obj) const
{
  return m_boxMin == obj.m_boxMin
      && m_boxMax == obj.m_boxMax;
}

void Box2D::swap(Box2D & obj)
{
  std::swap(m_boxMin, obj.m_boxMin);
  std::swap(m_boxMax, obj.m_boxMax);
}

bool Box2D::checkInside(const Box2D & box, const Point2D & point)
{
  return ((point.x() >= box.m_boxMin.x() &&
       point.x() <= box.m_boxMax.x() &&
       point.y() >= box.m_boxMin.y() &&
       point.y() <= box.m_boxMax.y()) ||
      (point.x() >= box.m_boxMax.x() &&
       point.x() <= box.m_boxMin.x() &&
       point.y() >= box.m_boxMax.y() &&
       point.y() <= box.m_boxMin.y())
      );
}

bool Box2D::checkBoxes(const Box2D & box1, const Box2D & box2)
{
  // If two boxes are not intersected with each other
  // then return false.
  return not ( box1.boxMax().x() <= box2.boxMin().x() ||
           box2.boxMax().x() <= box1.boxMin().x() ||
           box1.boxMin().y() >= box2.boxMax().y() ||
           box2.boxMin().y() >= box1.boxMax().y() );
}

Box2D Box2D::createBox(const Point2D & minPoint, const Point2D & maxPoint)
{
  if(minPoint.x()>=maxPoint.x() || minPoint.y()>=maxPoint.y())
    throw std::invalid_argument("You must create correct box");
  return Box2D(minPoint,maxPoint);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Eugene Lazin <4lazin@gmail.com>
 *
 * 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 2 of the License, or (at your option)
 * any later version.
 *
 */

#include <thread>
#include "akumuli_def.h"
#include "cache.h"
#include "util.h"

namespace Akumuli {

// This method must be called from the same thread
int Sequence::add(TimeStamp ts, ParamId param, EntryOffset  offset) noexcept {
    auto key = std::make_tuple(ts, param);

    std::unique_lock<std::mutex> lock(obj_mtx_, std::defer_lock)
                               , tmp_lock(tmp_mtx_, std::defer_lock);

    if (lock.try_lock()) {
        if (tmp_lock.try_lock()) {
            for(auto const& tup: temp_) {
                auto tkey = std::make_tuple(std::get<0>(tup), std::get<1>(tup));
                data_.insert(std::make_pair(tkey, std::get<2>(tup)));
            }
            tmp_lock.unlock();
        }
        data_.insert(std::make_pair(key, offset));
    } else {
        tmp_lock.lock();
        temp_.emplace_back(ts, param, offset);
    }
    return AKU_WRITE_STATUS_SUCCESS;
}

void Sequence::search(Caller& caller, InternalCursor* cursor, SearchQuery const& query) const noexcept {

    bool forward = query.direction == AKU_CURSOR_DIR_FORWARD;
    bool backward = query.direction == AKU_CURSOR_DIR_BACKWARD;

    if (query.upperbound < query.lowerbound  // Right timestamps and
        || !(forward ^ backward)             // right direction constant
    ) {
        cursor->set_error(caller, AKU_EBAD_ARG);
        return;
    }

    if (backward)
    {
        auto tskey = query.upperbound;
        auto idkey = (ParamId)~0;
        auto key = std::make_tuple(tskey, idkey);
        auto citer = data_.upper_bound(key);
        auto last_key = std::make_tuple(query.lowerbound, 0);

        std::unique_lock<std::mutex> lock(obj_mtx_);
        while(true) {
            auto& curr_key = citer->first;
            if (std::get<0>(curr_key) <= std::get<0>(last_key)) {
                break;
            }
            if (query.param_pred(std::get<1>(curr_key)) == SearchQuery::MATCH && std::get<0>(curr_key) <= tskey) {
                cursor->put(caller, citer->second);
            }
            if (citer == data_.begin()) {
                break;
            }
            citer--;
        }
    }
    else
    {
        auto tskey = query.lowerbound;
        auto idkey = (ParamId)0;
        auto key = std::make_tuple(tskey, idkey);
        auto citer = data_.lower_bound(key);
        auto last_key = std::make_tuple(query.upperbound, ~0);

        std::unique_lock<std::mutex> lock(obj_mtx_);
        while(citer != data_.end()) {
            auto& curr_key = citer->first;
            if (std::get<0>(curr_key) >= std::get<0>(last_key)) {
                break;
            }
            if (query.param_pred(std::get<1>(curr_key)) == SearchQuery::MATCH) {
                cursor->put(caller, citer->second);
            }
            citer++;
        }
    }
    cursor->complete(caller);
}

size_t Sequence::size() const noexcept {
    return data_.size();
}

Sequence::MapType::const_iterator Sequence::begin() const {
    return data_.begin();
}

Sequence::MapType::const_iterator Sequence::end() const {
    return data_.end();
}

// Bucket -------------------------------------

Bucket::Bucket(int64_t size_limit, int64_t baseline)
    : baseline(baseline)
    , limit_(size_limit)
    , state(0)
{
}

int Bucket::add(TimeStamp ts, ParamId param, EntryOffset  offset) noexcept {
    if (limit_.dec()) {
        return seq_.local().add(ts, param, offset);
    }
    return AKU_EOVERFLOW;
}

void Bucket::search(Caller &caller, InternalCursor* cursor, SearchQuery const& query) const noexcept {
    for(SeqList::iterator i = seq_.begin(); i != seq_.end(); i++) {
        i->search(caller, cursor, query);
    }
}

typedef Sequence::MapType::const_iterator iter_t;

static bool less_than(iter_t lhs, iter_t rhs) {
    return  lhs->first < rhs->first;
}

int Bucket::merge(Caller& caller, InternalCursor *cur) const noexcept {

    if (state.load() == 0) {
        return AKU_EBUSY;
    }

    size_t n = seq_.size();

    // Init
    iter_t iter[n], end[n];
    size_t cnt = 0u;
    for(SeqList::iterator i = seq_.begin(); i != seq_.end(); i++) {
        iter[cnt] = i->begin();
        end[cnt] = i->end();
        cnt++;
    }

    // Merge
    if (n > 1) {
        int next_min = 0;
        while(true) {
            int min = next_min;
            int op_cnt = 0;
            for (int i = 0; i < n; i++) {
                if (i == min) continue;
                if (iter[i] != end[i]) {
                    if (less_than(iter[i], iter[min])) {
                        next_min = min;
                        min = i;
                    } else {
                        next_min = i;
                    }
                    op_cnt++;
                }
            }
            if (op_cnt == 0)
                break;
            auto offset = iter[min]->second;
            cur->put(caller, offset);
            std::advance(iter[min], 1);
        }
        assert(iter == end);
    } else {
        for (const auto& pair: boost::make_iterator_range(iter[0], end[0])) {
            cur->put(caller, pair.second);
        }
    }
    return AKU_SUCCESS;
}

size_t Bucket::precise_count() const noexcept {
    return limit_.precise();
}

// Cache --------------------------------------

Cache::Cache(TimeDuration ttl, size_t max_size)
    : ttl_(ttl)
    , max_size_(max_size)
    , baseline_()
{
    // We need to calculate shift width. So, we got ttl in some units
    // of measure (units of measure that akumuli doesn't know about).
    shift_ = log2(ttl.value);
    if ((1 << shift_) < AKU_LIMITS_MIN_TTL) {
        throw std::runtime_error("TTL is too small");
    }
}

template<class TCont>
Sequence* index2ptr(TCont& cont, int64_t index) noexcept {
    auto begin = cont.begin();
    std::advance(begin, index);
    auto& gen = *begin;
    return &gen;
}

template<class TCont>
Sequence const* index2ptr(TCont const& cont, int64_t index) noexcept {
    auto begin = cont.cbegin();
    std::advance(begin, index);
    auto& gen = *begin;
    return &gen;
}

int Cache::add_entry_(TimeStamp ts, ParamId pid, EntryOffset offset, size_t* nswapped) noexcept {
    // NOTE: If it is less than zero - we need to shift cache.
    // Otherwise we can select existing generation but this can result in late write
    // or overflow.
    auto absolute_index = (ts.value >> shift_);

    TableType::accessor accessor;
    if (cache_.find(accessor, absolute_index)) {
        if (accessor->second->state == 0) {
            return accessor->second->add(ts, pid, offset);
        }
    }
    else {
        std::lock_guard<LockType> guard(lock_);
        auto rel_index = baseline_ - absolute_index;
        if (!cache_.find(accessor, absolute_index)) {
            if (rel_index > AKU_LIMITS_MAX_CACHES) {
                return AKU_ELATE_WRITE;
            }
            if (rel_index < 0) {
                // Future write! Mark all outdated buckets.
                auto size = cache_.size();
                auto min_baseline = absolute_index - AKU_LIMITS_MAX_CACHES;
                for(auto& b: live_buckets_) {
                    if (b->baseline < min_baseline && b->state.load() == 0) {
                        b->state++;
                        *nswapped += 1;
                    }
                }
            }
            // bucket is not already created by another thread
            baseline_ = absolute_index;
            size_t bucket_size = sizeof(Bucket);
            Bucket* new_bucket = allocator_.allocate(bucket_size);
            allocator_.construct(new_bucket, (int64_t)max_size_, baseline_);
            cache_.insert(std::make_pair(absolute_index, new_bucket));
            live_buckets_.push_front(new_bucket);
            return new_bucket->add(ts, pid, offset);
        }
        else {
            if (accessor->second->state == 0) {
                return accessor->second->add(ts, pid, offset);
            }
        }
    }
    return AKU_ELATE_WRITE;
}

int Cache::add_entry(const Entry& entry, EntryOffset offset, size_t* nswapped) noexcept {
    return add_entry_(entry.time, entry.param_id, offset, nswapped);
}

int Cache::add_entry(const Entry2& entry, EntryOffset offset, size_t* nswapped) noexcept {
    return add_entry_(entry.time, entry.param_id, offset, nswapped);
}

void Cache::clear() noexcept {
}

int Cache::pick_last(EntryOffset* offsets, size_t size, size_t* noffsets) noexcept {
    // fastpath - check all params
    if (size == 0 || offsets == nullptr)
        return AKU_EBAD_ARG;

    // get one bucket at a time under lock
    std::unique_lock<LockType> lock(lock_);
    Bucket* bucket = live_buckets_.back();
    *noffsets = bucket->precise_count();
    if (*noffsets > size)
        // Buffer is to small
        return AKU_ENO_MEM;
    BufferedCursor cursor(offsets, size);
    Caller caller;
    int status = bucket->merge(caller, &cursor);
    if (status == AKU_SUCCESS)
        live_buckets_.pop_back();
    else
        *noffsets = 0;
    return status;
}

void Cache::search(Caller& caller, InternalCursor *cur, SearchQuery& query) const noexcept {

    bool forward = query.direction == AKU_CURSOR_DIR_FORWARD;
    bool backward = query.direction == AKU_CURSOR_DIR_BACKWARD;

    if (query.upperbound < query.lowerbound
        || !(forward ^ backward)
    ) {
        // Invalid direction or timestamps
        cur->set_error(caller, AKU_EBAD_ARG);
        return;
    }

    if (forward) {
        auto tsbegin = query.lowerbound.value;
        auto keybegin = tsbegin >> shift_;
        std::vector<int64_t> indexes;
        {
            std::lock_guard<LockType> guard(lock_);
            auto index = baseline_ - keybegin;
            for(auto i = index; i < index + AKU_CACHE_POPULATION; i++) {
                indexes.push_back(i);
            }
        }
        for (auto ix: indexes) {
            TableType::accessor accessor;
            if (this->cache_.find(accessor, ix)) {
                accessor->second->search(caller, cur, query);
            }
        }
    }
}

}  // namespace Akumuli
<commit_msg>Bucket::search reimplemented (needs some unit testing)<commit_after>/*
 * Copyright (c) 2013 Eugene Lazin <4lazin@gmail.com>
 *
 * 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 2 of the License, or (at your option)
 * any later version.
 *
 */

#include <thread>
#include "akumuli_def.h"
#include "cache.h"
#include "util.h"

namespace Akumuli {

// This method must be called from the same thread
int Sequence::add(TimeStamp ts, ParamId param, EntryOffset  offset) noexcept {
    auto key = std::make_tuple(ts, param);

    std::unique_lock<std::mutex> lock(obj_mtx_, std::defer_lock)
                               , tmp_lock(tmp_mtx_, std::defer_lock);

    if (lock.try_lock()) {
        if (tmp_lock.try_lock()) {
            for(auto const& tup: temp_) {
                auto tkey = std::make_tuple(std::get<0>(tup), std::get<1>(tup));
                data_.insert(std::make_pair(tkey, std::get<2>(tup)));
            }
            tmp_lock.unlock();
        }
        data_.insert(std::make_pair(key, offset));
    } else {
        tmp_lock.lock();
        temp_.emplace_back(ts, param, offset);
    }
    return AKU_WRITE_STATUS_SUCCESS;
}

void Sequence::search(Caller& caller, InternalCursor* cursor, SearchQuery const& query) const noexcept {

    bool forward = query.direction == AKU_CURSOR_DIR_FORWARD;
    bool backward = query.direction == AKU_CURSOR_DIR_BACKWARD;

    if (query.upperbound < query.lowerbound  // Right timestamps and
        || !(forward ^ backward)             // right direction constant
    ) {
        cursor->set_error(caller, AKU_EBAD_ARG);
        return;
    }

    if (backward)
    {
        auto tskey = query.upperbound;
        auto idkey = (ParamId)~0;
        auto key = std::make_tuple(tskey, idkey);
        auto citer = data_.upper_bound(key);
        auto last_key = std::make_tuple(query.lowerbound, 0);

        std::unique_lock<std::mutex> lock(obj_mtx_);
        while(true) {
            auto& curr_key = citer->first;
            if (std::get<0>(curr_key) <= std::get<0>(last_key)) {
                break;
            }
            if (query.param_pred(std::get<1>(curr_key)) == SearchQuery::MATCH && std::get<0>(curr_key) <= tskey) {
                cursor->put(caller, citer->second);
            }
            if (citer == data_.begin()) {
                break;
            }
            citer--;
        }
    }
    else
    {
        auto tskey = query.lowerbound;
        auto idkey = (ParamId)0;
        auto key = std::make_tuple(tskey, idkey);
        auto citer = data_.lower_bound(key);
        auto last_key = std::make_tuple(query.upperbound, ~0);

        std::unique_lock<std::mutex> lock(obj_mtx_);
        while(citer != data_.end()) {
            auto& curr_key = citer->first;
            if (std::get<0>(curr_key) >= std::get<0>(last_key)) {
                break;
            }
            if (query.param_pred(std::get<1>(curr_key)) == SearchQuery::MATCH) {
                cursor->put(caller, citer->second);
            }
            citer++;
        }
    }
    cursor->complete(caller);
}

size_t Sequence::size() const noexcept {
    return data_.size();
}

Sequence::MapType::const_iterator Sequence::begin() const {
    return data_.begin();
}

Sequence::MapType::const_iterator Sequence::end() const {
    return data_.end();
}

// Bucket -------------------------------------

Bucket::Bucket(int64_t size_limit, int64_t baseline)
    : baseline(baseline)
    , limit_(size_limit)
    , state(0)
{
}

int Bucket::add(TimeStamp ts, ParamId param, EntryOffset  offset) noexcept {
    if (limit_.dec()) {
        return seq_.local().add(ts, param, offset);
    }
    return AKU_EOVERFLOW;
}

void Bucket::search(Caller &caller, InternalCursor* cursor, SearchQuery const& query) const noexcept {
    // NOTE: Quick and dirty implementation that copies all the local data
    // to temporary sequence.
    std::unique_ptr<Sequence> seq(new Sequence());
    for(SeqList::iterator i = seq_.begin(); i != seq_.end(); i++) {
        std::unique_lock<std::mutex> lock(i->obj_mtx_);
        for (auto local = i->begin(); local != i->end(); local++) {
            seq->add(std::get<0>(local->first), std::get<1>(local->first), local->second);
        }
    }
    seq->search(caller, cursor, query);
}

typedef Sequence::MapType::const_iterator iter_t;

static bool less_than(iter_t lhs, iter_t rhs) {
    return  lhs->first < rhs->first;
}

int Bucket::merge(Caller& caller, InternalCursor *cur) const noexcept {

    if (state.load() == 0) {
        return AKU_EBUSY;
    }

    size_t n = seq_.size();

    // Init
    iter_t iter[n], end[n];
    size_t cnt = 0u;
    for(SeqList::iterator i = seq_.begin(); i != seq_.end(); i++) {
        iter[cnt] = i->begin();
        end[cnt] = i->end();
        cnt++;
    }

    // Merge
    if (n > 1) {
        int next_min = 0;
        while(true) {
            int min = next_min;
            int op_cnt = 0;
            for (int i = 0; i < n; i++) {
                if (i == min) continue;
                if (iter[i] != end[i]) {
                    if (less_than(iter[i], iter[min])) {
                        next_min = min;
                        min = i;
                    } else {
                        next_min = i;
                    }
                    op_cnt++;
                }
            }
            if (op_cnt == 0)
                break;
            auto offset = iter[min]->second;
            cur->put(caller, offset);
            std::advance(iter[min], 1);
        }
        assert(iter == end);
    } else {
        for (const auto& pair: boost::make_iterator_range(iter[0], end[0])) {
            cur->put(caller, pair.second);
        }
    }
    return AKU_SUCCESS;
}

size_t Bucket::precise_count() const noexcept {
    return limit_.precise();
}

// Cache --------------------------------------

Cache::Cache(TimeDuration ttl, size_t max_size)
    : ttl_(ttl)
    , max_size_(max_size)
    , baseline_()
{
    // We need to calculate shift width. So, we got ttl in some units
    // of measure (units of measure that akumuli doesn't know about).
    shift_ = log2(ttl.value);
    if ((1 << shift_) < AKU_LIMITS_MIN_TTL) {
        throw std::runtime_error("TTL is too small");
    }
}

template<class TCont>
Sequence* index2ptr(TCont& cont, int64_t index) noexcept {
    auto begin = cont.begin();
    std::advance(begin, index);
    auto& gen = *begin;
    return &gen;
}

template<class TCont>
Sequence const* index2ptr(TCont const& cont, int64_t index) noexcept {
    auto begin = cont.cbegin();
    std::advance(begin, index);
    auto& gen = *begin;
    return &gen;
}

int Cache::add_entry_(TimeStamp ts, ParamId pid, EntryOffset offset, size_t* nswapped) noexcept {
    // NOTE: If it is less than zero - we need to shift cache.
    // Otherwise we can select existing generation but this can result in late write
    // or overflow.
    auto absolute_index = (ts.value >> shift_);

    TableType::accessor accessor;
    if (cache_.find(accessor, absolute_index)) {
        if (accessor->second->state == 0) {
            return accessor->second->add(ts, pid, offset);
        }
    }
    else {
        std::lock_guard<LockType> guard(lock_);
        auto rel_index = baseline_ - absolute_index;
        if (!cache_.find(accessor, absolute_index)) {
            if (rel_index > AKU_LIMITS_MAX_CACHES) {
                return AKU_ELATE_WRITE;
            }
            if (rel_index < 0) {
                // Future write! Mark all outdated buckets.
                auto size = cache_.size();
                auto min_baseline = absolute_index - AKU_LIMITS_MAX_CACHES;
                for(auto& b: live_buckets_) {
                    if (b->baseline < min_baseline && b->state.load() == 0) {
                        b->state++;
                        *nswapped += 1;
                    }
                }
            }
            // bucket is not already created by another thread
            baseline_ = absolute_index;
            size_t bucket_size = sizeof(Bucket);
            Bucket* new_bucket = allocator_.allocate(bucket_size);
            allocator_.construct(new_bucket, (int64_t)max_size_, baseline_);
            cache_.insert(std::make_pair(absolute_index, new_bucket));
            live_buckets_.push_front(new_bucket);
            return new_bucket->add(ts, pid, offset);
        }
        else {
            if (accessor->second->state == 0) {
                return accessor->second->add(ts, pid, offset);
            }
        }
    }
    return AKU_ELATE_WRITE;
}

int Cache::add_entry(const Entry& entry, EntryOffset offset, size_t* nswapped) noexcept {
    return add_entry_(entry.time, entry.param_id, offset, nswapped);
}

int Cache::add_entry(const Entry2& entry, EntryOffset offset, size_t* nswapped) noexcept {
    return add_entry_(entry.time, entry.param_id, offset, nswapped);
}

void Cache::clear() noexcept {
}

int Cache::pick_last(EntryOffset* offsets, size_t size, size_t* noffsets) noexcept {
    // fastpath - check all params
    if (size == 0 || offsets == nullptr)
        return AKU_EBAD_ARG;

    // get one bucket at a time under lock
    std::unique_lock<LockType> lock(lock_);
    Bucket* bucket = live_buckets_.back();
    *noffsets = bucket->precise_count();
    if (*noffsets > size)
        // Buffer is to small
        return AKU_ENO_MEM;
    BufferedCursor cursor(offsets, size);
    Caller caller;
    int status = bucket->merge(caller, &cursor);
    if (status == AKU_SUCCESS)
        live_buckets_.pop_back();
    else
        *noffsets = 0;
    return status;
}

void Cache::search(Caller& caller, InternalCursor *cur, SearchQuery& query) const noexcept {

    bool forward = query.direction == AKU_CURSOR_DIR_FORWARD;
    bool backward = query.direction == AKU_CURSOR_DIR_BACKWARD;

    if (query.upperbound < query.lowerbound
        || !(forward ^ backward)
    ) {
        // Invalid direction or timestamps
        cur->set_error(caller, AKU_EBAD_ARG);
        return;
    }

    if (forward) {
        auto tsbegin = query.lowerbound.value;
        auto keybegin = tsbegin >> shift_;
        std::vector<int64_t> indexes;
        {
            std::lock_guard<LockType> guard(lock_);
            auto index = baseline_ - keybegin;
            for(auto i = index; i < index + AKU_CACHE_POPULATION; i++) {
                indexes.push_back(i);
            }
        }
        for (auto ix: indexes) {
            TableType::accessor accessor;
            if (this->cache_.find(accessor, ix)) {
                accessor->second->search(caller, cur, query);
            }
        }
    }
}

}  // namespace Akumuli
<|endoftext|>