<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "personalization.hxx"

#include <comphelper/processfactory.hxx>
#include <officecfg/Office/Common.hxx>
#include <vcl/msgbox.hxx>

#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/system/SystemShellExecute.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
#include <com/sun/star/ui/dialogs/XFilePicker.hpp>
#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
#include <com/sun/star/ui/dialogs/XFilterManager.hpp>

using namespace com::sun::star;

/** Dialog that will allow the user to choose a Persona to use.

So far there is no better possibility than just to paste the URL from
http://www.getpersona.com ...
*/
class SelectPersonaDialog : public ModalDialog
{
public:
    SelectPersonaDialog( Window *pParent );

private:
    /// Handle the [Visit Firefox Personas] button
    DECL_LINK( VisitPersonas, PushButton* );
};

SelectPersonaDialog::SelectPersonaDialog( Window *pParent )
    : ModalDialog( pParent, "SelectPersonaDialog", "cui/ui/select_persona_dialog.ui" )
{
    PushButton *pButton;
    get( pButton, "visit_personas" );
    pButton->SetClickHdl( LINK( this, SelectPersonaDialog, VisitPersonas ) );
}

IMPL_LINK( SelectPersonaDialog, VisitPersonas, PushButton*, /*pButton*/ )
{
    uno::Reference< system::XSystemShellExecute > xSystemShell( system::SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) );

    xSystemShell->execute( "http://www.getpersonas.com", OUString(), system::SystemShellExecuteFlags::URIS_ONLY );

    return 0;
}

SvxPersonalizationTabPage::SvxPersonalizationTabPage( Window *pParent, const SfxItemSet &rSet )
    : SfxTabPage( pParent, "PersonalizationTabPage", "cui/ui/personalization_tab.ui", rSet ),
      m_aBackgroundURL()
{
    // background image
    get( m_pNoBackground, "no_background" );
    get( m_pDefaultBackground, "default_background" );
    get( m_pOwnBackground, "own_background" );

    get( m_pSelectBackground, "select_background" );
    m_pSelectBackground->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectBackground ) );

    // persona
    get( m_pNoPersona, "no_persona" );
    get( m_pDefaultPersona, "default_persona" );
    get( m_pOwnPersona, "own_persona" );

    get( m_pSelectPersona, "select_persona" );
    LINK( this, SvxPersonalizationTabPage, SelectPersona );
    m_pSelectPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectPersona ) );
}

SvxPersonalizationTabPage::~SvxPersonalizationTabPage()
{
}

SfxTabPage* SvxPersonalizationTabPage::Create( Window *pParent, const SfxItemSet &rSet )
{
    return new SvxPersonalizationTabPage( pParent, rSet );
}

sal_Bool SvxPersonalizationTabPage::FillItemSet( SfxItemSet & )
{
    // background image
    OUString aBackground( "default" );
    if ( m_pNoBackground->IsChecked() )
        aBackground = "no";
    else if ( m_pOwnBackground->IsChecked() )
        aBackground = "own";

    // persona
    OUString aPersona( "default" );
    if ( m_pNoPersona->IsChecked() )
        aPersona = "no";
    else if ( m_pOwnPersona->IsChecked() )
        aPersona = "own";

    bool bModified = false;
    uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
    if ( xContext.is() &&
            ( aBackground != officecfg::Office::Common::Misc::BackgroundImage::get( xContext ) ||
              m_aBackgroundURL != officecfg::Office::Common::Misc::BackgroundImageURL::get( xContext ) ||
              aPersona != officecfg::Office::Common::Misc::Persona::get( xContext ) ) )
    {
        bModified = true;
    }

    // write
    boost::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );

    officecfg::Office::Common::Misc::BackgroundImage::set( aBackground, batch );
    officecfg::Office::Common::Misc::BackgroundImageURL::set( m_aBackgroundURL, batch );
    officecfg::Office::Common::Misc::Persona::set( aPersona, batch );

    batch->commit();

    return bModified;
}

void SvxPersonalizationTabPage::Reset( const SfxItemSet & )
{
    uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();

    // background image
    OUString aBackground( "default" );
    if ( xContext.is() )
    {
        aBackground = officecfg::Office::Common::Misc::BackgroundImage::get( xContext );
        m_aBackgroundURL = officecfg::Office::Common::Misc::BackgroundImageURL::get( xContext );
    }

    if ( aBackground == "no" )
        m_pNoBackground->Check();
    else if ( aBackground == "own" )
        m_pOwnBackground->Check();
    else
        m_pDefaultBackground->Check();

    // persona
    OUString aPersona( "default" );
    if ( xContext.is() )
        aPersona = officecfg::Office::Common::Misc::Persona::get( xContext );

    if ( aPersona == "no" )
        m_pNoPersona->Check();
    else if ( aPersona == "own" )
        m_pOwnPersona->Check();
    else
        m_pDefaultPersona->Check();
}

IMPL_LINK( SvxPersonalizationTabPage, SelectBackground, PushButton*, /*pButton*/ )
{
    uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
    if ( !xFactory.is() )
        return 0;

    uno::Reference< ui::dialogs::XFilePicker > xFilePicker( xFactory->createInstance( "com.sun.star.ui.dialogs.FilePicker" ), uno::UNO_QUERY );
    if ( !xFilePicker.is() )
        return 0;

    xFilePicker->setMultiSelectionMode( false );

    uno::Reference< ui::dialogs::XFilePickerControlAccess > xController( xFilePicker, uno::UNO_QUERY );
    if ( xController.is() )
        xController->setValue( ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, uno::makeAny( sal_True ) );

    uno::Reference< ui::dialogs::XFilterManager > xFilterMgr( xFilePicker, uno::UNO_QUERY );
    if ( xFilterMgr.is() )
        xFilterMgr->appendFilter( "Background images (*.jpg;*.png)", "*.jpg;*.png" ); // TODO localize

    while ( xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK )
    {
        OUString aFile( xFilePicker->getFiles()[0] );

        if ( aFile.startsWith( "file:///" ) && ( aFile.endsWith( ".png" ) || aFile.endsWith( ".jpg" ) ) )
        {
            m_aBackgroundURL = aFile;
            m_pOwnBackground->Check();
            break;
        }

        // TODO display what is wrong (not a file:// or not .png / .jpg)
    }

    return 0;
}

IMPL_LINK( SvxPersonalizationTabPage, SelectPersona, PushButton*, /*pButton*/ )
{
    SelectPersonaDialog aDialog( NULL );

    if ( aDialog.Execute() == RET_OK )
    {
        m_pOwnPersona->Check();
        // TODO parse the results
    }

    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Personas: Allow to paste Persona to the selection dialog.<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "personalization.hxx"

#include <comphelper/processfactory.hxx>
#include <officecfg/Office/Common.hxx>
#include <vcl/edit.hxx>
#include <vcl/msgbox.hxx>

#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/system/SystemShellExecute.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
#include <com/sun/star/ui/dialogs/XFilePicker.hpp>
#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
#include <com/sun/star/ui/dialogs/XFilterManager.hpp>

using namespace com::sun::star;

/** Dialog that will allow the user to choose a Persona to use.

So far there is no better possibility than just to paste the URL from
http://www.getpersona.com ...
*/
class SelectPersonaDialog : public ModalDialog
{
private:
    Edit *m_pEdit;                          ///< The input line for the Persona URL

public:
    SelectPersonaDialog( Window *pParent );

    /// Get the URL from the Edit field.
    OUString GetPersonaURL() const;

private:
    /// Handle the [Visit Firefox Personas] button
    DECL_LINK( VisitPersonas, PushButton* );
};

SelectPersonaDialog::SelectPersonaDialog( Window *pParent )
    : ModalDialog( pParent, "SelectPersonaDialog", "cui/ui/select_persona_dialog.ui" )
{
    PushButton *pButton;
    get( pButton, "visit_personas" );
    pButton->SetClickHdl( LINK( this, SelectPersonaDialog, VisitPersonas ) );

    get( m_pEdit, "persona_url" );
    m_pEdit->SetPlaceholderText( "http://www.getpersonas.com/persona/" );
}

OUString SelectPersonaDialog::GetPersonaURL() const
{
    OUString aText( m_pEdit->GetText() );

    if ( !aText.startsWith( "http://www.getpersonas.com/" ) )
        return OUString();

    return aText;
}

IMPL_LINK( SelectPersonaDialog, VisitPersonas, PushButton*, /*pButton*/ )
{
    uno::Reference< system::XSystemShellExecute > xSystemShell( system::SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) );

    xSystemShell->execute( "http://www.getpersonas.com", OUString(), system::SystemShellExecuteFlags::URIS_ONLY );

    return 0;
}

SvxPersonalizationTabPage::SvxPersonalizationTabPage( Window *pParent, const SfxItemSet &rSet )
    : SfxTabPage( pParent, "PersonalizationTabPage", "cui/ui/personalization_tab.ui", rSet ),
      m_aBackgroundURL()
{
    // background image
    get( m_pNoBackground, "no_background" );
    get( m_pDefaultBackground, "default_background" );
    get( m_pOwnBackground, "own_background" );

    get( m_pSelectBackground, "select_background" );
    m_pSelectBackground->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectBackground ) );

    // persona
    get( m_pNoPersona, "no_persona" );
    get( m_pDefaultPersona, "default_persona" );
    get( m_pOwnPersona, "own_persona" );

    get( m_pSelectPersona, "select_persona" );
    LINK( this, SvxPersonalizationTabPage, SelectPersona );
    m_pSelectPersona->SetClickHdl( LINK( this, SvxPersonalizationTabPage, SelectPersona ) );
}

SvxPersonalizationTabPage::~SvxPersonalizationTabPage()
{
}

SfxTabPage* SvxPersonalizationTabPage::Create( Window *pParent, const SfxItemSet &rSet )
{
    return new SvxPersonalizationTabPage( pParent, rSet );
}

sal_Bool SvxPersonalizationTabPage::FillItemSet( SfxItemSet & )
{
    // background image
    OUString aBackground( "default" );
    if ( m_pNoBackground->IsChecked() )
        aBackground = "no";
    else if ( m_pOwnBackground->IsChecked() )
        aBackground = "own";

    // persona
    OUString aPersona( "default" );
    if ( m_pNoPersona->IsChecked() )
        aPersona = "no";
    else if ( m_pOwnPersona->IsChecked() )
        aPersona = "own";

    bool bModified = false;
    uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
    if ( xContext.is() &&
            ( aBackground != officecfg::Office::Common::Misc::BackgroundImage::get( xContext ) ||
              m_aBackgroundURL != officecfg::Office::Common::Misc::BackgroundImageURL::get( xContext ) ||
              aPersona != officecfg::Office::Common::Misc::Persona::get( xContext ) ) )
    {
        bModified = true;
    }

    // write
    boost::shared_ptr< comphelper::ConfigurationChanges > batch( comphelper::ConfigurationChanges::create() );

    officecfg::Office::Common::Misc::BackgroundImage::set( aBackground, batch );
    officecfg::Office::Common::Misc::BackgroundImageURL::set( m_aBackgroundURL, batch );
    officecfg::Office::Common::Misc::Persona::set( aPersona, batch );

    batch->commit();

    return bModified;
}

void SvxPersonalizationTabPage::Reset( const SfxItemSet & )
{
    uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();

    // background image
    OUString aBackground( "default" );
    if ( xContext.is() )
    {
        aBackground = officecfg::Office::Common::Misc::BackgroundImage::get( xContext );
        m_aBackgroundURL = officecfg::Office::Common::Misc::BackgroundImageURL::get( xContext );
    }

    if ( aBackground == "no" )
        m_pNoBackground->Check();
    else if ( aBackground == "own" )
        m_pOwnBackground->Check();
    else
        m_pDefaultBackground->Check();

    // persona
    OUString aPersona( "default" );
    if ( xContext.is() )
        aPersona = officecfg::Office::Common::Misc::Persona::get( xContext );

    if ( aPersona == "no" )
        m_pNoPersona->Check();
    else if ( aPersona == "own" )
        m_pOwnPersona->Check();
    else
        m_pDefaultPersona->Check();
}

IMPL_LINK( SvxPersonalizationTabPage, SelectBackground, PushButton*, /*pButton*/ )
{
    uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
    if ( !xFactory.is() )
        return 0;

    uno::Reference< ui::dialogs::XFilePicker > xFilePicker( xFactory->createInstance( "com.sun.star.ui.dialogs.FilePicker" ), uno::UNO_QUERY );
    if ( !xFilePicker.is() )
        return 0;

    xFilePicker->setMultiSelectionMode( false );

    uno::Reference< ui::dialogs::XFilePickerControlAccess > xController( xFilePicker, uno::UNO_QUERY );
    if ( xController.is() )
        xController->setValue( ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW, 0, uno::makeAny( sal_True ) );

    uno::Reference< ui::dialogs::XFilterManager > xFilterMgr( xFilePicker, uno::UNO_QUERY );
    if ( xFilterMgr.is() )
        xFilterMgr->appendFilter( "Background images (*.jpg;*.png)", "*.jpg;*.png" ); // TODO localize

    while ( xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK )
    {
        OUString aFile( xFilePicker->getFiles()[0] );

        if ( aFile.startsWith( "file:///" ) && ( aFile.endsWith( ".png" ) || aFile.endsWith( ".jpg" ) ) )
        {
            m_aBackgroundURL = aFile;
            m_pOwnBackground->Check();
            break;
        }

        // TODO display what is wrong (not a file:// or not .png / .jpg)
    }

    return 0;
}

IMPL_LINK( SvxPersonalizationTabPage, SelectPersona, PushButton*, /*pButton*/ )
{
    SelectPersonaDialog aDialog( NULL );

    while ( aDialog.Execute() == RET_OK )
    {
        OUString aURL( aDialog.GetPersonaURL() );
        if ( aURL != "" )
        {
            // TODO parse the results
            m_pOwnPersona->Check();
            break;
        }
        // else TODO msgbox that the URL did not match
    }

    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2002 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <iostream>
#include <fstream>
#include "OggAudioFile.h"

OggAudioFile::OggAudioFile(std::istream* in) : AudioFile(in)
{
	stream = -1;

	ov_callbacks cb;
	cb.read_func = OAFRead;
	cb.seek_func = OAFSeek;
	cb.close_func = OAFClose;
	cb.tell_func = OAFTell;

	OAFInputBundle* bundle = new OAFInputBundle;
	in->seekg(0, std::ios::end);
	bundle->input = in; bundle->length = std::streamoff(in->tellg());
	in->seekg(0, std::ios::beg);

	if(ov_open_callbacks(bundle, &file, NULL, 0, cb) < 0) {
		std::cout << "OggAudioFile() failed: call to ov_open_callbacks failed\n";
	}
	else {
		info = ov_info(&file, -1);
		int samples = ov_pcm_total(&file, -1);
		std::cout << ov_streams(&file) << " logical bitstreams\n";
		init(info->rate, info->channels, samples, 2);
	}
}

OggAudioFile::~OggAudioFile()
{
	ov_clear(&file);
}

std::string	OggAudioFile::getExtension()
{
	return ".ogg";
}

bool		OggAudioFile::read(void* buffer, int numFrames)
{
	int frames;
	int bytes = numFrames * info->channels * 2;
	ogg_int64_t oldoff = ov_pcm_tell(&file);
#if BYTE_ORDER == BIG_ENDIAN
	frames = ov_read(&file, (char *) buffer, bytes, 1, 2, 1, &stream);
#else
	frames = ov_read(&file, (char *) buffer, bytes, 0, 2, 1, &stream);
#endif
	ogg_int64_t pcmoff = ov_pcm_tell(&file);
	std::cout << "requested: " << numFrames << "\n";
	std::cout << "actual:    " << (pcmoff  - oldoff) << "\n\n";
	if (frames < 0) {
		if (frames == OV_HOLE)
			// OV_HOLE is non-fatal
			return true;
		else
			return false;
	}
	return true;
}

size_t	OAFRead(void* ptr, size_t size, size_t nmemb, void* datasource)
{
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	std::streamoff pos1 = std::streamoff(bundle->input->tellg());
	std::streamsize read = size * nmemb;

	if (pos1 + read > bundle->length) {
		read = bundle->length - pos1;
	}
	if (pos1 == bundle->length)
		// EOF
		return 0;
	bundle->input->read((char*) ptr, read);

	return read;
}

int		OAFSeek(void* datasource, ogg_int64_t offset, int whence)
{
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	switch (whence) {
		case SEEK_SET:
			bundle->input->seekg(offset, std::ios::beg);
			break;
		case SEEK_CUR:
			bundle->input->seekg(offset, std::ios::cur);
			break;
		case SEEK_END:
			bundle->input->seekg(offset, std::ios::end);
			break;
	}
	return 0;
}

int		OAFClose(void* datasource)
{
	// technically we should close here, but this is handled outside
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	delete bundle;
	return 0;
}

long		OAFTell(void* datasource)
{
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	return bundle->input->tellg();
}
// ex: shiftwidth=4 tabstop=4
<commit_msg>read more than the first packet<commit_after>/* bzflag
 * Copyright (c) 1993 - 2002 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <iostream>
#include <fstream>
#include "OggAudioFile.h"

OggAudioFile::OggAudioFile(std::istream* in) : AudioFile(in)
{
	stream = -1;

	ov_callbacks cb;
	cb.read_func = OAFRead;
	cb.seek_func = OAFSeek;
	cb.close_func = OAFClose;
	cb.tell_func = OAFTell;

	OAFInputBundle* bundle = new OAFInputBundle;
	in->seekg(0, ios::end);
	bundle->input = in; bundle->length = std::streamoff(in->tellg());
	in->seekg(0, ios::beg);

	if(ov_open_callbacks(bundle, &file, NULL, 0, cb) < 0) {
		std::cout << "OggAudioFile() failed: call to ov_open_callbacks failed\n";
	}
	else {
		info = ov_info(&file, -1);
		int samples = ov_pcm_total(&file, -1);
		std::cout << ov_streams(&file) << " logical bitstreams\n";
		init(info->rate, info->channels, samples, 2);
	}
}

OggAudioFile::~OggAudioFile()
{
	ov_clear(&file);
}

std::string	OggAudioFile::getExtension()
{
	return ".ogg";
}

bool		OggAudioFile::read(void* buffer, int numFrames)
{
	int result;
	long bytestotal = numFrames * info->channels * 2;
	while (bytestotal > 0) {
		long oldoff = ov_pcm_tell(&file) * info->channels * 2;
		long bytesleft = bytestotal - oldoff;
#if BYTE_ORDER == BIG_ENDIAN
		result = ov_read(&file, ((char*) buffer) + oldoff, bytesleft, 1, 2, 1, &stream);
#else
		result = ov_read(&file, ((char*) buffer) + oldoff, bytesleft, 0, 2, 1, &stream);
#endif
		long newoff = ov_pcm_tell(&file) * info->channels * 2;
		long bytesread = newoff - oldoff;
		if (result == OV_EBADLINK)
			return false;
		else if (result == 0)
			return false;
		bytestotal -= bytesread;
	}
	return true;
}

size_t	OAFRead(void* ptr, size_t size, size_t nmemb, void* datasource)
{
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	std::streamoff pos1 = std::streamoff(bundle->input->tellg());
	std::streamsize read = size * nmemb;

	if (pos1 + read > bundle->length) {
		read = bundle->length - pos1;
	}
	if (pos1 == bundle->length)
		// EOF
		return 0;
	bundle->input->read((char*) ptr, read);

	return read;
}

int		OAFSeek(void* datasource, ogg_int64_t offset, int whence)
{
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	switch (whence) {
		case SEEK_SET:
			bundle->input->seekg(offset, std::ios::beg);
			break;
		case SEEK_CUR:
			bundle->input->seekg(offset, std::ios::cur);
			break;
		case SEEK_END:
			bundle->input->seekg(offset, std::ios::end);
			break;
	}
	return 0;
}

int		OAFClose(void* datasource)
{
	// technically we should close here, but this is handled outside
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	delete bundle;
	return 0;
}

long		OAFTell(void* datasource)
{
	OAFInputBundle* bundle = (OAFInputBundle*) datasource;
	return bundle->input->tellg();
}
// ex: shiftwidth=4 tabstop=4
<|endoftext|>


<commit_before>// Tests for the RooDataSet
// Authors: Stephan Hageboeck, CERN  04/2020

#include "RooDataSet.h"
#include "RooRealVar.h"
#include "RooHelpers.h"
#include "TTree.h"

#include "gtest/gtest.h"

/// ROOT-10676
/// The RooDataSet warns that it's not using all variables if the selection string doesn't
/// make use of all variables. Although true, the user has no way to suppress this.
TEST(RooDataSet, ImportFromTreeWithCut)
{
  RooHelpers::HijackMessageStream hijack(RooFit::INFO, RooFit::InputArguments);

  TTree tree("tree", "tree");
  double thex, they;
  tree.Branch("x", &thex);
  tree.Branch("y", &they);
  tree.Branch("z", &they);
  thex = -0.337;
  they = 1.;
  tree.Fill();

  thex = 0.337;
  they = 1.;
  tree.Fill();

  thex = 1.337;
  they = 1.;
  tree.Fill();

  RooRealVar x("x", "x", 0);
  RooRealVar y("y", "y", 0);
  RooRealVar z("z", "z", 0);
  RooDataSet data("data", "data", &tree, RooArgSet(x, y, z), "x>y");

  EXPECT_TRUE(hijack.str().empty()) << "Messages issued were: " << hijack.str();
  EXPECT_EQ(data.numEntries(), 1);

  RooRealVar* theX = dynamic_cast<RooRealVar*>(data.get(0)->find("x"));
  ASSERT_NE(theX, nullptr);
  EXPECT_FLOAT_EQ(theX->getVal(), 1.337);
}
<commit_msg>[RF] Add tests ROOT-4580 and ROOT-9528.<commit_after>// Tests for the RooDataSet
// Authors: Stephan Hageboeck, CERN  04/2020

#include "RooDataSet.h"
#include "RooDataHist.h"
#include "RooRealVar.h"
#include "RooHelpers.h"
#include "TTree.h"

#include <TRandom3.h>
#include <TH1F.h>
#include <TCut.h>

#include "gtest/gtest.h"

/// ROOT-10676
/// The RooDataSet warns that it's not using all variables if the selection string doesn't
/// make use of all variables. Although true, the user has no way to suppress this.
TEST(RooDataSet, ImportFromTreeWithCut)
{
  RooHelpers::HijackMessageStream hijack(RooFit::INFO, RooFit::InputArguments);

  TTree tree("tree", "tree");
  double thex, they;
  tree.Branch("x", &thex);
  tree.Branch("y", &they);
  tree.Branch("z", &they);
  thex = -0.337;
  they = 1.;
  tree.Fill();

  thex = 0.337;
  they = 1.;
  tree.Fill();

  thex = 1.337;
  they = 1.;
  tree.Fill();

  RooRealVar x("x", "x", 0);
  RooRealVar y("y", "y", 0);
  RooRealVar z("z", "z", 0);
  RooDataSet data("data", "data", &tree, RooArgSet(x, y, z), "x>y");

  EXPECT_TRUE(hijack.str().empty()) << "Messages issued were: " << hijack.str();
  EXPECT_EQ(data.numEntries(), 1);

  RooRealVar* theX = dynamic_cast<RooRealVar*>(data.get(0)->find("x"));
  ASSERT_NE(theX, nullptr);
  EXPECT_FLOAT_EQ(theX->getVal(), 1.337);
}

/// ROOT-9528 Branch names are capped after a certain number of characters
TEST(RooDataSet, ImportLongBranchNames) {

  TTree tree("theTree", "theTree");
  double doub = 0.;
  tree.Branch("HLT_mu6_mu4_bBmumux_BsmumuPhi_delayed_L1BPH_2M8_MU6MU4_BPH_0DR15_MU6MU4", &doub);
  doub = 2.;
  tree.Fill();
  doub = 4.;
  tree.Fill();

  RooRealVar *v = new RooRealVar("HLT_mu6_mu4_bBmumux_BsmumuPhi_delayed_L1BPH_2M8_MU6MU4_BPH_0DR15_MU6MU4", "HLT_mu6_mu4_bBmumux_BsmumuPhi_delayed_L1BPH_2M8_MU6MU4_BPH_0DR15_MU6MU4", 0., -100000., 100000.);

  RooDataSet ds("ds", "ds", RooArgSet(*v), RooFit::Import(tree));
  EXPECT_EQ(static_cast<RooRealVar*>(ds.get(0)->find(*v))->getVal(), 2.);
  EXPECT_EQ(static_cast<RooRealVar*>(ds.get(1)->find(*v))->getVal(), 4.);
  EXPECT_EQ(ds.numEntries(), 2);
  EXPECT_DOUBLE_EQ(ds.sumEntries("HLT_mu6_mu4_bBmumux_BsmumuPhi_delayed_L1BPH_2M8_MU6MU4_BPH_0DR15_MU6MU4 > 3."), 1.);
}

/// ROOT-4580, possibly solved by ROOT-10517
TEST(RooDataSet, ReducingData) {
  //Test Data hist and such.
  TTree mytree("tree","tree") ;
  double mass_x, track0_chi2_x, track1_chi2_x;

  mytree.Branch("track0_chi2",&track0_chi2_x,"track0_chi2/D");
  mytree.Branch("track1_chi2",&track1_chi2_x,"track1_chi2/D");
  mytree.Branch("mass",&mass_x,"mass/D");
  for (int i=0; i < 50; i++) {
    track0_chi2_x = gRandom->Landau(1,0.5);
    track1_chi2_x = gRandom->Landau(1,0.5);
    mass_x = gRandom->Gaus(20, 0.5);
    mytree.Fill() ;
  }

  Double_t chi2cutval = 1.0;
  constexpr Double_t massmin = 0;
  constexpr Double_t massmax = 40;

  //Now use roofit
  //observables from ttree
  RooRealVar mymass("mass", "mass", massmin, massmax);
  RooRealVar track0_chi2("track0_chi2", "track0_chi2", -10., 90);
  RooRealVar track1_chi2("track1_chi2", "track1_chi2", -10., 90);

  //get the datasets
  RooDataSet *data_unbinned = new RooDataSet("mass_example","mass example", &mytree, RooArgSet(mymass,track0_chi2,track1_chi2));
  std::unique_ptr<RooDataHist> data( data_unbinned->binnedClone("data") );

  for (int i=0; i<3; ++i){
    // Check with root:
    TH1F test_hist(Form("h%i", i), "histo", 10, massmin, massmax);
    chi2cutval+=0.5;

    TCut chi2_test_cut = Form("max(track0_chi2,track1_chi2)<%f",chi2cutval);

    Long64_t drawnEvents = mytree.Draw(Form("mass>>h%i", i), chi2_test_cut /*&& mass_cut*/);
    ASSERT_NE(drawnEvents, 0l);
    ASSERT_EQ(test_hist.Integral(), drawnEvents);

    // For unbinned data, reducing should be equivalent to the tree.
    std::unique_ptr<RooDataSet> data_unbinned_reduced ( static_cast<RooDataSet*>(data_unbinned->reduce(RooFit::Cut(chi2_test_cut))) );
    EXPECT_DOUBLE_EQ(data_unbinned_reduced->sumEntries(), test_hist.Integral());
    EXPECT_EQ(data_unbinned_reduced->numEntries(), test_hist.Integral());

    // When using binned data, reducing and expecting the ame number of entries as in the unbinned case is not possible,
    // since information is lost if entries to the left and right of the cut end up in the same bin.
    // Therefore, can only test <=
    std::unique_ptr<RooDataHist> reduced_binned_data ( static_cast<RooDataHist*>(data->reduce(RooFit::Cut(chi2_test_cut))) );
    if (floor(chi2cutval) == chi2cutval)
      EXPECT_FLOAT_EQ(reduced_binned_data->sumEntries(), test_hist.Integral());
    else
      EXPECT_LE(reduced_binned_data->sumEntries(), test_hist.Integral());
  }
}

<|endoftext|>


<commit_before>#pragma once

#include <agency/detail/config.hpp>
#include <agency/detail/tuple.hpp>
#include <type_traits>

namespace agency
{

template<class T>
struct decay_construct_result : std::decay<T> {};

template<class T>
using decay_construct_result_t = typename decay_construct_result<T>::type;

template<class... Types>
struct decay_construct_result<
  detail::tuple<Types...>
>
{
  using type = detail::tuple<typename decay_construct_result<Types>::type...>;
};

template<class T>
__AGENCY_ANNOTATION
decay_construct_result_t<T> decay_construct(T&& parm)
{
  // this is essentially decay_copy
  return std::forward<T>(parm);
}

namespace detail
{

template<class T, class... Args>
class factory
{
  public:
    __AGENCY_ANNOTATION
    factory(const tuple<Args...>& args)
      : args_(args)
    {}

    __AGENCY_ANNOTATION
    T make() const &
    {
      return __tu::make_from_tuple<T>(args_);
    }

    __AGENCY_ANNOTATION
    T make() &&
    {
      return __tu::make_from_tuple<T>(std::move(args_));
    }

  private:
    tuple<Args...> args_;
};

template<size_t level, class T, class... Args>
struct shared_parameter : public factory<T,Args...>
{
  using factory<T,Args...>::factory;
};

template<class T> struct is_shared_parameter : std::false_type {};
template<size_t level, class T, class... Args>
struct is_shared_parameter<shared_parameter<level,T,Args...>> : std::true_type {};

template<class T>
struct is_shared_parameter_ref
  : std::integral_constant<
      bool,
      (std::is_reference<T>::value && is_shared_parameter<typename std::remove_reference<T>::type>::value)
    >
{};

} // end detail

template<size_t level, class T, class... Args>
struct decay_construct_result<
  detail::shared_parameter<level, T, Args...>
>
{
  using type = T;
};

// overload decay_construct for shared_parameter
template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
T decay_construct(detail::shared_parameter<level,T,Args...>& parm)
{
  return parm.make();
}

template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
T decay_construct(const detail::shared_parameter<level,T,Args...>& parm)
{
  return parm.make();
}

template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
T decay_construct(detail::shared_parameter<level,T,Args...>&& parm)
{
  return std::move(parm).make();
}

template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
detail::shared_parameter<level, T,Args...> share(Args&&... args)
{
  return detail::shared_parameter<level, T,Args...>{detail::make_tuple(std::forward<Args>(args)...)};
}

template<size_t level, class T>
__AGENCY_ANNOTATION
detail::shared_parameter<level,T,T> share(const T& val)
{
  return detail::shared_parameter<level,T,T>{detail::make_tuple(val)};
}

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>& t);

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(const detail::tuple<Types...>& t);

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>&& t);

namespace detail
{

struct call_decay_construct
{
  template<class T>
  __AGENCY_ANNOTATION
  auto operator()(T&& arg) const
    -> decltype(
         agency::decay_construct(std::forward<T>(arg))
       )
  {
    return agency::decay_construct(std::forward<T>(arg));
  }
};

} // end detail

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>& t)
{
  return detail::tuple_map(
    detail::call_decay_construct{}, t
  );
}

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(const detail::tuple<Types...>& t)
{
  return detail::tuple_map(
    detail::call_decay_construct{}, t
  );
}

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>&& t)
{
  return detail::tuple_map(
    detail::call_decay_construct{}, std::move(t)
  );
}

} // end agency

<commit_msg>Introduce agency::invoke()<commit_after>#pragma once

#include <agency/detail/config.hpp>
#include <agency/detail/tuple.hpp>
#include <utility>
#include <type_traits>

namespace agency
{

template<class T>
struct decay_construct_result : std::decay<T> {};

template<class T>
using decay_construct_result_t = typename decay_construct_result<T>::type;

template<class... Types>
struct decay_construct_result<
  detail::tuple<Types...>
>
{
  using type = detail::tuple<typename decay_construct_result<Types>::type...>;
};

template<class T>
__AGENCY_ANNOTATION
decay_construct_result_t<T> decay_construct(T&& parm)
{
  // this is essentially decay_copy
  return std::forward<T>(parm);
}

namespace detail
{

template<class T, class... Args>
class factory
{
  public:
    __AGENCY_ANNOTATION
    factory(const tuple<Args...>& args)
      : args_(args)
    {}

    __AGENCY_ANNOTATION
    T make() const &
    {
      return __tu::make_from_tuple<T>(args_);
    }

    __AGENCY_ANNOTATION
    T make() &&
    {
      return __tu::make_from_tuple<T>(std::move(args_));
    }

  private:
    tuple<Args...> args_;
};

template<size_t level, class T, class... Args>
struct shared_parameter : public factory<T,Args...>
{
  using factory<T,Args...>::factory;
};

template<class T> struct is_shared_parameter : std::false_type {};
template<size_t level, class T, class... Args>
struct is_shared_parameter<shared_parameter<level,T,Args...>> : std::true_type {};

template<class T>
struct is_shared_parameter_ref
  : std::integral_constant<
      bool,
      (std::is_reference<T>::value && is_shared_parameter<typename std::remove_reference<T>::type>::value)
    >
{};

} // end detail

template<size_t level, class T, class... Args>
struct decay_construct_result<
  detail::shared_parameter<level, T, Args...>
>
{
  using type = T;
};

// overload decay_construct for shared_parameter
template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
T decay_construct(detail::shared_parameter<level,T,Args...>& parm)
{
  return parm.make();
}

template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
T decay_construct(const detail::shared_parameter<level,T,Args...>& parm)
{
  return parm.make();
}

template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
T decay_construct(detail::shared_parameter<level,T,Args...>&& parm)
{
  return std::move(parm).make();
}

template<size_t level, class T, class... Args>
__AGENCY_ANNOTATION
detail::shared_parameter<level, T,Args...> share(Args&&... args)
{
  return detail::shared_parameter<level, T,Args...>{detail::make_tuple(std::forward<Args>(args)...)};
}

template<size_t level, class T>
__AGENCY_ANNOTATION
detail::shared_parameter<level,T,T> share(const T& val)
{
  return detail::shared_parameter<level,T,T>{detail::make_tuple(val)};
}

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>& t);

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(const detail::tuple<Types...>& t);

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>&& t);

namespace detail
{

struct call_decay_construct
{
  template<class T>
  __AGENCY_ANNOTATION
  auto operator()(T&& arg) const
    -> decltype(
         agency::decay_construct(std::forward<T>(arg))
       )
  {
    return agency::decay_construct(std::forward<T>(arg));
  }
};

} // end detail

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>& t)
{
  return detail::tuple_map(
    detail::call_decay_construct{}, t
  );
}

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(const detail::tuple<Types...>& t)
{
  return detail::tuple_map(
    detail::call_decay_construct{}, t
  );
}

template<class... Types>
__AGENCY_ANNOTATION
decay_construct_result_t<detail::tuple<Types...>> decay_construct(detail::tuple<Types...>&& t)
{
  return detail::tuple_map(
    detail::call_decay_construct{}, std::move(t)
  );
}

__agency_hd_warning_disable__
template<class F, class... Args>
inline __AGENCY_ANNOTATION
auto invoke(F&& f, Args&&... args) ->
  decltype(std::forward<F>(f)(std::forward<Args>(args)...))
{
  return std::forward<F>(f)(std::forward<Args>(args)...);
};

} // end agency

<|endoftext|>


<commit_before>#ifndef __FAST5_SUMMARY_HPP
#define __FAST5_SUMMARY_HPP

#include <array>
#include <string>
#include <vector>

#ifndef H5_HAVE_THREADSAFE
#include <mutex>
#endif

#include "Pore_Model.hpp"
#include "fast5.hpp"
#include "alg.hpp"

template < typename Float_Type = float >
class Fast5_Summary
{
public:
    typedef Pore_Model< Float_Type > Pore_Model_Type;
    typedef Pore_Model_Parameters< Float_Type > Pore_Model_Parameters_Type;
    typedef Event< Float_Type > Event_Type;
    typedef Event_Sequence< Float_Type > Event_Sequence_Type;

    std::string file_name;
    std::string base_file_name;
    std::string read_id;
    std::array< std::string, 2 > preferred_model;
    std::array< std::map< std::string, Pore_Model_Parameters_Type >, 2 > params;
    std::array< unsigned, 4 > strand_bounds;
    unsigned num_ed_events;
    float abasic_level;
    bool have_ed_events;
    bool valid;

    static unsigned& min_read_len()
    {
        static unsigned _min_read_len = 1000;
        return _min_read_len;
    }

    // from fast5 file
    std::vector< fast5::EventDetection_Event_Entry > ed_events;
    // filtered
    std::array< Event_Sequence_Type, 2 > events;

    Fast5_Summary() : valid(false) {}
    Fast5_Summary(const std::string fn, const Pore_Model_Dict_Type& models) : valid(false) { summarize(fn, models); }

    void summarize(const std::string& fn, const Pore_Model_Dict_Type& models)
    {
        valid = true;
        file_name = fn;
        auto pos = file_name.find_last_of('/');
        base_file_name = (pos != std::string::npos? file_name.substr(pos + 1) : file_name);
        if (base_file_name.substr(base_file_name.size() - 6) == ".fast5")
        {
            base_file_name.resize(base_file_name.size() - 6);
        }
        fast5::File f(file_name);
        have_ed_events = f.have_eventdetection_events();
        num_ed_events = 0;
        abasic_level = 0.0;
        if (have_ed_events)
        {
            load_ed_events(f);
            auto ed_params = f.get_eventdetection_event_parameters();
            num_ed_events = ed_events.size();
            read_id = ed_params.read_id;
            if (read_id.empty())
            {
                read_id = base_file_name;
            }
            abasic_level = detect_abasic_level();
            if (abasic_level > 1.0)
            {
                detect_strands();
            }
            // compute initial model scalings
            load_events();
            for (unsigned st = 0; st < 2; ++st)
            {
                if (events[st].size() < min_read_len())
                {
                    continue;
                }
                auto r = alg::mean_stdv_of< Float_Type >(events[st], [] (const Event_Type& ev) { return ev.mean; });
                for (const auto& p : models)
                {
                    if (p.second.strand() == st or p.second.strand() == 2)
                    {
                        Pore_Model_Parameters_Type param;
                        param.scale = r.second / p.second.stdv();
                        param.shift = r.first - param.scale * p.second.mean();
                        LOG("Fast5_Summary", debug)
                            << "read [" << read_id << "] strand [" << st
                            << "] model [" << p.first << "] initial parameters "
                            << param << std::endl;
                        params[st][p.first] = std::move(param);
                    }
                }
            }
            ed_events.clear();
        }
    }

    void load_events()
    {
        assert(valid and have_ed_events);
        if (ed_events.empty())
        {
#ifndef H5_HAVE_THREADSAFE
            static std::mutex fast5_mutex;
            std::lock_guard< std::mutex > fast5_lock(fast5_mutex);
#endif
            fast5::File f(file_name);
            load_ed_events(f);
            f.close();
        }
        for (unsigned st = 0; st < 2; ++st)
        {
            events[st].clear();
            if (strand_bounds[2 * st + 0] == 0) continue;
            for (unsigned j = strand_bounds[2 * st + 0]; j < strand_bounds[2 * st + 1]; ++j)
            {
                if (filter_ed_event(ed_events[j], abasic_level))
                {
                    Event_Type e;
                    e.mean = ed_events[j].mean;
                    e.stdv = ed_events[j].stdv;
                    e.start = 0.0;
                    e.length = 0.0;
                    e.update_logs();
                    events[st].push_back(e);
                }
            }
        }
        ed_events.clear();
    }
    void drop_events()
    {
        for (unsigned st = 0; st < 2; ++st)
        {
            events[st].clear();
        }
    }

    friend std::ostream& operator << (std::ostream& os, const Fast5_Summary& fs)
    {
        os << "[base_file_name=" << fs.base_file_name << " valid=" << fs.valid;
        if (fs.valid)
        {
            os << " have_ed_events=" << fs.have_ed_events;
            if (fs.have_ed_events)
            {
                os << " read_id=" << fs.read_id
                   << " abasic_level=" << fs.abasic_level
                   << " num_ed_events=" << fs.num_ed_events
                   << " strand_bounds=[" << fs.strand_bounds[0] << ","
                   << fs.strand_bounds[1] << ","
                   << fs.strand_bounds[2] << ","
                   << fs.strand_bounds[3] << "]";
            }
        }
        os << "]";
        return os;
    }

    void write_tsv(std::ostream& os) const
    {
        assert(have_ed_events);
        os << base_file_name << '\t' << read_id << '\t' << num_ed_events << '\t' << abasic_level
           << '\t' << strand_bounds[0] << '\t' << strand_bounds[1]
           << '\t' << strand_bounds[2] << '\t' << strand_bounds[3]
           << '\t' << preferred_model[0] << '\t' << preferred_model[1];
        for (unsigned st = 0; st < 2; ++st)
        {
            os << '\t';
            if (params[st].count(preferred_model[st]))
            {
                params[st].at(preferred_model[st]).write_tsv(os);
            }
            else
            {
                Pore_Model_Parameters_Type().write_tsv(os);
            }
        }
    }

private:
    void load_ed_events(fast5::File& f)
    {
        ed_events = f.get_eventdetection_events();
        auto ed_params = f.get_eventdetection_event_parameters();
    }

    // crude detection of abasic level
    Float_Type detect_abasic_level()
    {
        if (ed_events.size() < min_read_len())
        {
            return 0.0;
        }
        //
        // use 1.0 pA + max level excluding to 5%
        //
        std::vector< Float_Type > s;
        s.resize(ed_events.size());
        unsigned i;
        for (i = 0; i < ed_events.size(); ++i)
        {
            s[i] = ed_events[i].mean;
        }
        std::sort(s.begin(), s.end());
        return s[99 * s.size() / 100] + 5.0f;
    } // detect_abasic_level()

    // crude detection of abasic level
    Float_Type detect_abasic_level_2()
    {
        if (ed_events.size() < min_read_len())
        {
            return 0.0;
        }
        //
        // look for a peak level greater than the median,
        // such that the next peak below it is more than 1pA lower
        //
        std::vector< Float_Type > s;
        s.resize(ed_events.size());
        unsigned i;
        for (i = 0; i < ed_events.size(); ++i)
        {
            s[i] = ed_events[i].mean;
        }
        std::sort(s.begin(), s.end());
        i = s.size() / 2;
        while (i < s.size() and s[i - 1] > s[i] - 1.0) ++i;
        if (i >= s.size())
        {
            return 0.0;
        }
        return s[i];
    } // detect_abasic_level_2()

    // crude detection of strands in event sequence
    void detect_strands()
    {
        strand_bounds = { 50, static_cast< unsigned >(ed_events.size() - 50), 0, 0 };
        LOG("Fast5_Summary", debug)
            << "num_events=" << ed_events.size()
            << " abasic_level=" << abasic_level << std::endl;
        //
        // find islands of >= 5 consecutive events at high level
        //
        std::vector< std::pair< unsigned, unsigned > > islands;
        unsigned i = 0;
        while (i < ed_events.size())
        {
            if (ed_events[i].mean >= abasic_level)
            {
                unsigned j = i + 1;
                while (j < ed_events.size() and ed_events[j].mean >= abasic_level) ++j;
                if (j - i >= 5)
                {
                    islands.push_back(std::make_pair(i, j));
                    LOG("Fast5_Summary", debug) << "abasic_island [" << i << "," << j << "]" << std::endl;
                }
                i = j + 1;
            }
            else
            {
                ++i;
            }
        }
        //
        // merge islands within 50bp of each other
        //
        for (i = 1; i < islands.size(); ++i)
        {
            if (islands[i - 1].second + 50 >= islands[i].first)
            {
                LOG("Fast5_Summary", debug) << "merge_islands "
                          << "[" << islands[i - 1].first << "," << islands[i - 1].second << "] with "
                          << "[" << islands[i].first << "," << islands[i].second << "]" << std::endl;
                islands[i - 1].second = islands[i].second;
                islands.erase(islands.begin() + i);
                i = 0;
            }
        }
        LOG("Fast5_Summary", debug)
            << "final_islands: " << alg::os_join(
                islands, " ",
                [] (const std::pair< unsigned, unsigned >& p) {
                    std::ostringstream tmp;
                    tmp << "[" << p.first << "," << p.second << "]";
                    return tmp.str();
                }) << std::endl;
        if (islands.empty())
        {
            LOG("Fast5_Summary", info)
                << "template_only read_id=[" << read_id << "]" << std::endl;
            return;
        }
        //
        // pick island closest to the middle of the event sequence
        //
        auto dist_to_middle = [&] (const std::pair< unsigned, unsigned >& p) {
            return std::min((unsigned)std::abs((long)p.first - (long)ed_events.size() / 2),
                            (unsigned)std::abs((long)p.second - (long)ed_events.size() / 2));
        };
        auto it = alg::min_of(islands, dist_to_middle);
        // check island is in the middle third; if not, intepret it as template only
        if (dist_to_middle(*it) > ed_events.size() / 6)
        {
            LOG("Fast5_Summary", info)
                << "drop_read read_id=[" << read_id
                << "] islands=[" << alg::os_join(
                    islands, " ",
                    [] (const std::pair< unsigned, unsigned >& p) {
                        std::ostringstream tmp;
                        tmp << "[" << p.first << "," << p.second << "]";
                        return tmp.str();
                    })
                << "]" << std::endl;
            return;
        }
        else
        {
            LOG("Fast5_Summary", debug)
                << "hairpin_island [" << it->first << "," << it->second << "]" << std::endl;
            strand_bounds[0] = 50;
            if (islands[0].first < 100)
            {
                strand_bounds[0] = std::max(strand_bounds[0], islands[0].second);
            }
            strand_bounds[1] = it->first - 50;
            strand_bounds[2] = it->first + 50;
            strand_bounds[3] = ed_events.size() - 50;
            if (islands[islands.size() - 1].second > ed_events.size() - 100)
            {
                strand_bounds[3] = std::min(strand_bounds[3], islands[islands.size() - 1].first);
            }
        }
    } // detect_strands()

    // crude filtering of eventdetection events
    static bool filter_ed_event(const fast5::EventDetection_Event_Entry& e, Float_Type abasic_level)
    {
        if (e.mean >= abasic_level)
        {
            return false;
        }
        if (e.stdv > 4.0)
        {
            return false;
        }
        return true;
    } // filter_ed_event()
}; // struct Fast5_Summary

typedef Fast5_Summary<> Fast5_Summary_Type;

#endif
<commit_msg>fix: crash if number of ed events too small<commit_after>#ifndef __FAST5_SUMMARY_HPP
#define __FAST5_SUMMARY_HPP

#include <array>
#include <string>
#include <vector>

#ifndef H5_HAVE_THREADSAFE
#include <mutex>
#endif

#include "Pore_Model.hpp"
#include "fast5.hpp"
#include "alg.hpp"

template < typename Float_Type = float >
class Fast5_Summary
{
public:
    typedef Pore_Model< Float_Type > Pore_Model_Type;
    typedef Pore_Model_Parameters< Float_Type > Pore_Model_Parameters_Type;
    typedef Event< Float_Type > Event_Type;
    typedef Event_Sequence< Float_Type > Event_Sequence_Type;

    std::string file_name;
    std::string base_file_name;
    std::string read_id;
    std::array< std::string, 2 > preferred_model;
    std::array< std::map< std::string, Pore_Model_Parameters_Type >, 2 > params;
    std::array< unsigned, 4 > strand_bounds;
    unsigned num_ed_events;
    float abasic_level;
    bool have_ed_events;
    bool valid;

    static unsigned& min_read_len()
    {
        static unsigned _min_read_len = 1000;
        return _min_read_len;
    }

    // from fast5 file
    std::vector< fast5::EventDetection_Event_Entry > ed_events;
    // filtered
    std::array< Event_Sequence_Type, 2 > events;

    Fast5_Summary() : valid(false) {}
    Fast5_Summary(const std::string fn, const Pore_Model_Dict_Type& models) : valid(false) { summarize(fn, models); }

    void summarize(const std::string& fn, const Pore_Model_Dict_Type& models)
    {
        valid = true;
        file_name = fn;
        auto pos = file_name.find_last_of('/');
        base_file_name = (pos != std::string::npos? file_name.substr(pos + 1) : file_name);
        strand_bounds = { 0, 0, 0, 0 };
        if (base_file_name.substr(base_file_name.size() - 6) == ".fast5")
        {
            base_file_name.resize(base_file_name.size() - 6);
        }
        fast5::File f(file_name);
        have_ed_events = f.have_eventdetection_events();
        num_ed_events = 0;
        abasic_level = 0.0;
        if (have_ed_events)
        {
            load_ed_events(f);
            auto ed_params = f.get_eventdetection_event_parameters();
            num_ed_events = ed_events.size();
            read_id = ed_params.read_id;
            if (read_id.empty())
            {
                read_id = base_file_name;
            }
            abasic_level = detect_abasic_level();
            if (abasic_level > 1.0)
            {
                // compute initial model scalings
                detect_strands();
                load_events();
                for (unsigned st = 0; st < 2; ++st)
                {
                    if (events[st].size() < min_read_len())
                    {
                        continue;
                    }
                    auto r = alg::mean_stdv_of< Float_Type >(events[st], [] (const Event_Type& ev) { return ev.mean; });
                    for (const auto& p : models)
                    {
                        if (p.second.strand() == st or p.second.strand() == 2)
                        {
                            Pore_Model_Parameters_Type param;
                            param.scale = r.second / p.second.stdv();
                            param.shift = r.first - param.scale * p.second.mean();
                            LOG("Fast5_Summary", debug)
                                << "read [" << read_id << "] strand [" << st
                                << "] model [" << p.first << "] initial parameters "
                                << param << std::endl;
                            params[st][p.first] = std::move(param);
                        }
                    }
                }
                ed_events.clear();
            } // if abasic_level > 1.0
        } // if have_ed_events
    }

    void load_events()
    {
        assert(valid and have_ed_events);
        if (ed_events.empty())
        {
#ifndef H5_HAVE_THREADSAFE
            static std::mutex fast5_mutex;
            std::lock_guard< std::mutex > fast5_lock(fast5_mutex);
#endif
            fast5::File f(file_name);
            load_ed_events(f);
            f.close();
        }
        for (unsigned st = 0; st < 2; ++st)
        {
            events[st].clear();
            if (strand_bounds[2 * st + 0] == 0) continue;
            for (unsigned j = strand_bounds[2 * st + 0]; j < strand_bounds[2 * st + 1]; ++j)
            {
                if (filter_ed_event(ed_events[j], abasic_level))
                {
                    Event_Type e;
                    e.mean = ed_events[j].mean;
                    e.stdv = ed_events[j].stdv;
                    e.start = 0.0;
                    e.length = 0.0;
                    e.update_logs();
                    events[st].push_back(e);
                }
            }
        }
        ed_events.clear();
    }
    void drop_events()
    {
        for (unsigned st = 0; st < 2; ++st)
        {
            events[st].clear();
        }
    }

    friend std::ostream& operator << (std::ostream& os, const Fast5_Summary& fs)
    {
        os << "[base_file_name=" << fs.base_file_name << " valid=" << fs.valid;
        if (fs.valid)
        {
            os << " have_ed_events=" << fs.have_ed_events;
            if (fs.have_ed_events)
            {
                os << " read_id=" << fs.read_id
                   << " abasic_level=" << fs.abasic_level
                   << " num_ed_events=" << fs.num_ed_events
                   << " strand_bounds=[" << fs.strand_bounds[0] << ","
                   << fs.strand_bounds[1] << ","
                   << fs.strand_bounds[2] << ","
                   << fs.strand_bounds[3] << "]";
            }
        }
        os << "]";
        return os;
    }

    void write_tsv(std::ostream& os) const
    {
        assert(have_ed_events);
        os << base_file_name << '\t' << read_id << '\t' << num_ed_events << '\t' << abasic_level
           << '\t' << strand_bounds[0] << '\t' << strand_bounds[1]
           << '\t' << strand_bounds[2] << '\t' << strand_bounds[3]
           << '\t' << preferred_model[0] << '\t' << preferred_model[1];
        for (unsigned st = 0; st < 2; ++st)
        {
            os << '\t';
            if (params[st].count(preferred_model[st]))
            {
                params[st].at(preferred_model[st]).write_tsv(os);
            }
            else
            {
                Pore_Model_Parameters_Type().write_tsv(os);
            }
        }
    }

private:
    void load_ed_events(fast5::File& f)
    {
        ed_events = f.get_eventdetection_events();
        auto ed_params = f.get_eventdetection_event_parameters();
    }

    // crude detection of abasic level
    Float_Type detect_abasic_level()
    {
        if (ed_events.size() < min_read_len())
        {
            return 0.0;
        }
        //
        // use 1.0 pA + max level excluding to 5%
        //
        std::vector< Float_Type > s;
        s.resize(ed_events.size());
        unsigned i;
        for (i = 0; i < ed_events.size(); ++i)
        {
            s[i] = ed_events[i].mean;
        }
        std::sort(s.begin(), s.end());
        return s[99 * s.size() / 100] + 5.0f;
    } // detect_abasic_level()

    // crude detection of abasic level
    Float_Type detect_abasic_level_2()
    {
        if (ed_events.size() < min_read_len())
        {
            return 0.0;
        }
        //
        // look for a peak level greater than the median,
        // such that the next peak below it is more than 1pA lower
        //
        std::vector< Float_Type > s;
        s.resize(ed_events.size());
        unsigned i;
        for (i = 0; i < ed_events.size(); ++i)
        {
            s[i] = ed_events[i].mean;
        }
        std::sort(s.begin(), s.end());
        i = s.size() / 2;
        while (i < s.size() and s[i - 1] > s[i] - 1.0) ++i;
        if (i >= s.size())
        {
            return 0.0;
        }
        return s[i];
    } // detect_abasic_level_2()

    // crude detection of strands in event sequence
    void detect_strands()
    {
        strand_bounds = { 50, static_cast< unsigned >(ed_events.size() - 50), 0, 0 };
        LOG("Fast5_Summary", debug)
            << "num_events=" << ed_events.size()
            << " abasic_level=" << abasic_level << std::endl;
        //
        // find islands of >= 5 consecutive events at high level
        //
        std::vector< std::pair< unsigned, unsigned > > islands;
        unsigned i = 0;
        while (i < ed_events.size())
        {
            if (ed_events[i].mean >= abasic_level)
            {
                unsigned j = i + 1;
                while (j < ed_events.size() and ed_events[j].mean >= abasic_level) ++j;
                if (j - i >= 5)
                {
                    islands.push_back(std::make_pair(i, j));
                    LOG("Fast5_Summary", debug) << "abasic_island [" << i << "," << j << "]" << std::endl;
                }
                i = j + 1;
            }
            else
            {
                ++i;
            }
        }
        //
        // merge islands within 50bp of each other
        //
        for (i = 1; i < islands.size(); ++i)
        {
            if (islands[i - 1].second + 50 >= islands[i].first)
            {
                LOG("Fast5_Summary", debug) << "merge_islands "
                          << "[" << islands[i - 1].first << "," << islands[i - 1].second << "] with "
                          << "[" << islands[i].first << "," << islands[i].second << "]" << std::endl;
                islands[i - 1].second = islands[i].second;
                islands.erase(islands.begin() + i);
                i = 0;
            }
        }
        LOG("Fast5_Summary", debug)
            << "final_islands: " << alg::os_join(
                islands, " ",
                [] (const std::pair< unsigned, unsigned >& p) {
                    std::ostringstream tmp;
                    tmp << "[" << p.first << "," << p.second << "]";
                    return tmp.str();
                }) << std::endl;
        if (islands.empty())
        {
            LOG("Fast5_Summary", info)
                << "template_only read_id=[" << read_id << "]" << std::endl;
            return;
        }
        //
        // pick island closest to the middle of the event sequence
        //
        auto dist_to_middle = [&] (const std::pair< unsigned, unsigned >& p) {
            return std::min((unsigned)std::abs((long)p.first - (long)ed_events.size() / 2),
                            (unsigned)std::abs((long)p.second - (long)ed_events.size() / 2));
        };
        auto it = alg::min_of(islands, dist_to_middle);
        // check island is in the middle third; if not, intepret it as template only
        if (dist_to_middle(*it) > ed_events.size() / 6)
        {
            LOG("Fast5_Summary", info)
                << "drop_read read_id=[" << read_id
                << "] islands=[" << alg::os_join(
                    islands, " ",
                    [] (const std::pair< unsigned, unsigned >& p) {
                        std::ostringstream tmp;
                        tmp << "[" << p.first << "," << p.second << "]";
                        return tmp.str();
                    })
                << "]" << std::endl;
            return;
        }
        else
        {
            LOG("Fast5_Summary", debug)
                << "hairpin_island [" << it->first << "," << it->second << "]" << std::endl;
            strand_bounds[0] = 50;
            if (islands[0].first < 100)
            {
                strand_bounds[0] = std::max(strand_bounds[0], islands[0].second);
            }
            strand_bounds[1] = it->first - 50;
            strand_bounds[2] = it->first + 50;
            strand_bounds[3] = ed_events.size() - 50;
            if (islands[islands.size() - 1].second > ed_events.size() - 100)
            {
                strand_bounds[3] = std::min(strand_bounds[3], islands[islands.size() - 1].first);
            }
        }
    } // detect_strands()

    // crude filtering of eventdetection events
    static bool filter_ed_event(const fast5::EventDetection_Event_Entry& e, Float_Type abasic_level)
    {
        if (e.mean >= abasic_level)
        {
            return false;
        }
        if (e.stdv > 4.0)
        {
            return false;
        }
        return true;
    } // filter_ed_event()
}; // struct Fast5_Summary

typedef Fast5_Summary<> Fast5_Summary_Type;

#endif
<|endoftext|>


<commit_before>/**
 * @file consensushash.cpp
 *
 * This file contains the function to generate consensus hashes.
 */

#include "omnicore/consensushash.h"
#include "omnicore/dex.h"
#include "omnicore/mdex.h"
#include "omnicore/log.h"
#include "omnicore/omnicore.h"
#include "omnicore/sp.h"

#include <stdint.h>
#include <string>

#include <openssl/sha.h>

namespace mastercore
{
/**
 * Calculates the total number of from the tally map
 *
 * Note - faster than using getTotalTokens() as doesn't require loading SP from database
 **/
int64_t GetTallyPropertyTotal(uint32_t propertyId)
{
    int64_t totalTokens = 0;
    for (std::map<std::string, CMPTally>::const_iterator it = mp_tally_map.begin(); it != mp_tally_map.end(); ++it) {
        const CMPTally& tally = it->second;
        totalTokens += tally.getMoney(propertyId, BALANCE);
        totalTokens += tally.getMoney(propertyId, SELLOFFER_RESERVE);
        totalTokens += tally.getMoney(propertyId, ACCEPT_RESERVE);
        totalTokens += tally.getMoney(propertyId, METADEX_RESERVE);
    }
    return totalTokens;
}

/**
 * Obtains a hash of the active state to use for consensus verification and checkpointing.
 *
 * For increased flexibility, so other implementations like OmniWallet and OmniChest can
 * also apply this methodology without necessarily using the same exact data types (which
 * would be needed to hash the data bytes directly), create a string in the following
 * format for each entry to use for hashing:
 *
 * ---STAGE 1 - BALANCES---
 * Format specifiers & placeholders:
 *   "%s|%d|%d|%d|%d|%d" - "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve"
 *
 * Note: empty balance records and the pending tally are ignored. Addresses are sorted based
 * on lexicographical order, and balance records are sorted by the property identifiers.
 *
 * ---STAGE 2 - DEX SELL OFFERS---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d|%d|%d|%d|%d" - "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit|availableamount|acceptedamount"
 *
 * Note: ordered ascending by txid.
 *
 * ---STAGE 3 - DEX ACCEPTS---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d" - "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock"
 *
 * Note: ordered ascending by matchedselloffertxid followed by buyer.
 *
 * ---STAGE 4 - METADEX TRADES---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d|%d|%d" - "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining"
 *
 * Note: ordered ascending by txid.
 *
 * ---STAGE 5 - CROWDSALES---
 * Format specifiers & placeholders:
 *   "%d|%d|%d|%d|%d" - "propertyid|propertyiddesired|deadline|usertokens|issuertokens"
 *
 * Note: ordered by property ID.
 *
 * ---STAGE 6 - PROPERTIES---
 * Format specifiers & placeholders:
 *   "%d|%d" - "nextavailablepropertyidmaineco|nextavailablepropertyidtesteco"
 *
 * The byte order is important, and we assume:
 *   SHA256("abc") = "ad1500f261ff10b49c7a1796a36103b02322ae5dde404141eacf018fbf1678ba"
 *
 */
uint256 GetConsensusHash()
{
    // allocate and init a SHA256_CTX
    SHA256_CTX shaCtx;
    SHA256_Init(&shaCtx);

    LOCK(cs_tally);

    if (msc_debug_consensus_hash) PrintToLog("Beginning generation of current consensus hash...\n");

    // Balances - loop through the tally map, updating the sha context with the data from each balance and tally type
    // Placeholders:  "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve"
    for (std::map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it) {
        const std::string& address = my_it->first;
        CMPTally& tally = my_it->second;
        tally.init();
        uint32_t propertyId = 0;
        while (0 != (propertyId = (tally.next()))) {
            int64_t balance = tally.getMoney(propertyId, BALANCE);
            int64_t sellOfferReserve = tally.getMoney(propertyId, SELLOFFER_RESERVE);
            int64_t acceptReserve = tally.getMoney(propertyId, ACCEPT_RESERVE);
            int64_t metaDExReserve = tally.getMoney(propertyId, METADEX_RESERVE);

            // skip this entry if all balances are empty
            if (!balance && !sellOfferReserve && !acceptReserve && !metaDExReserve) continue;

            std::string dataStr = strprintf("%s|%d|%d|%d|%d|%d",
                    address, propertyId, balance, sellOfferReserve, acceptReserve, metaDExReserve);
            if (msc_debug_consensus_hash) PrintToLog("Adding balance data to consensus hash: %s\n", dataStr);
            SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
        }
    }

    // DEx sell offers - loop through the DEx and add each sell offer to the consensus hash (ordered by txid)
    // Placeholders: "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit|availableamount|acceptedamount"
    std::vector<std::pair<uint256, std::string> > vecDExOffers;
    for (OfferMap::iterator it = my_offers.begin(); it != my_offers.end(); ++it) {
        const CMPOffer& selloffer = it->second;
        const std::string& sellCombo = it->first;
        uint32_t propertyId = selloffer.getProperty();
        std::string seller = sellCombo.substr(0, sellCombo.size() - 2);
        std::string dataStr = strprintf("%s|%s|%d|%d|%d|%d|%d|%d|%d",
                selloffer.getHash().GetHex(), seller, propertyId, selloffer.getOfferAmountOriginal(),
                selloffer.getBTCDesiredOriginal(), selloffer.getMinFee(), selloffer.getBlockTimeLimit(),
                getMPbalance(seller, propertyId, SELLOFFER_RESERVE), getMPbalance(seller, propertyId, ACCEPT_RESERVE));
        vecDExOffers.push_back(std::make_pair(selloffer.getHash(), dataStr));
    }
    std::sort (vecDExOffers.begin(), vecDExOffers.end());
    for (std::vector<std::pair<uint256, std::string> >::iterator it = vecDExOffers.begin(); it != vecDExOffers.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding DEx offer data to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // DEx accepts - loop through the accepts map and add each accept to the consensus hash (ordered by matchedtxid then buyer)
    // Placeholders: "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock"
    std::vector<std::pair<std::string, std::string> > vecAccepts;
    for (AcceptMap::const_iterator it = my_accepts.begin(); it != my_accepts.end(); ++it) {
        const CMPAccept& accept = it->second;
        const std::string& acceptCombo = it->first;
        std::string buyer = acceptCombo.substr((acceptCombo.find("+") + 1), (acceptCombo.size()-(acceptCombo.find("+") + 1)));
        std::string dataStr = strprintf("%s|%s|%d|%d|%d",
                accept.getHash().GetHex(), buyer, accept.getAcceptAmount(), accept.getAcceptAmountRemaining(), accept.getAcceptBlock());
        std::string sortKey = strprintf("%s-%s", accept.getHash().GetHex(), buyer);
        vecAccepts.push_back(std::make_pair(sortKey, dataStr));
    }
    std::sort (vecAccepts.begin(), vecAccepts.end());
    for (std::vector<std::pair<std::string, std::string> >::iterator it = vecAccepts.begin(); it != vecAccepts.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding DEx accept to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // MetaDEx trades - loop through the MetaDEx maps and add each open trade to the consensus hash (ordered by txid)
    // Placeholders: "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining"
    std::vector<std::pair<uint256, std::string> > vecMetaDExTrades;
    for (md_PropertiesMap::const_iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it) {
        const md_PricesMap& prices = my_it->second;
        for (md_PricesMap::const_iterator it = prices.begin(); it != prices.end(); ++it) {
            const md_Set& indexes = it->second;
            for (md_Set::const_iterator it = indexes.begin(); it != indexes.end(); ++it) {
                const CMPMetaDEx& obj = *it;
                std::string dataStr = strprintf("%s|%s|%d|%d|%d|%d|%d",
                    obj.getHash().GetHex(), obj.getAddr(), obj.getProperty(), obj.getAmountForSale(),
                    obj.getDesProperty(), obj.getAmountDesired(), obj.getAmountRemaining());
                    vecMetaDExTrades.push_back(std::make_pair(obj.getHash(), dataStr));
            }
        }
    }
    std::sort (vecMetaDExTrades.begin(), vecMetaDExTrades.end());
    for (std::vector<std::pair<uint256, std::string> >::iterator it = vecMetaDExTrades.begin(); it != vecMetaDExTrades.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding MetaDEx trade data to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // Crowdsales - loop through open crowdsales and add to the consensus hash (ordered by property ID)
    // Note: the variables of the crowdsale (amount, bonus etc) are not part of the crowdsale map and not included here to
    // avoid additionalal loading of SP entries from the database
    // Placeholders: "propertyid|propertyiddesired|deadline|usertokens|issuertokens"
    std::vector<std::pair<uint32_t, std::string> > vecCrowds;
    for (CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it) {
        const CMPCrowd& crowd = it->second;
        uint32_t propertyId = crowd.getPropertyId();
        std::string dataStr = strprintf("%d|%d|%d|%d|%d",
            crowd.getPropertyId(), crowd.getCurrDes(), crowd.getDeadline(), crowd.getUserCreated(), crowd.getIssuerCreated());
        vecCrowds.push_back(std::make_pair(propertyId, dataStr));
    }
    std::sort (vecCrowds.begin(), vecCrowds.end());
    for (std::vector<std::pair<uint32_t, std::string> >::iterator it = vecCrowds.begin(); it != vecCrowds.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding Crowdsale entry to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // Properties - add the next available property ID in both the main and test ecosystems
    // Placeholders: "nextavailablepropertyidmaineco|nextavailablepropertyidtesteco"
    std::string dataStr = strprintf("%d|%d", _my_sps->peekNextSPID(1), _my_sps->peekNextSPID(2));
    if (msc_debug_consensus_hash) PrintToLog("Adding property to consensus hash: %s\n", dataStr);
    SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());

    // extract the final result and return the hash
    uint256 consensusHash;
    SHA256_Final((unsigned char*)&consensusHash, &shaCtx);
    if (msc_debug_consensus_hash) PrintToLog("Finished generation of consensus hash.  Result: %s\n", consensusHash.GetHex());

    return consensusHash;
}

} // namespace mastercore
<commit_msg>Remove unused GetTallyPropertyTotal()<commit_after>/**
 * @file consensushash.cpp
 *
 * This file contains the function to generate consensus hashes.
 */

#include "omnicore/consensushash.h"
#include "omnicore/dex.h"
#include "omnicore/mdex.h"
#include "omnicore/log.h"
#include "omnicore/omnicore.h"
#include "omnicore/sp.h"

#include <stdint.h>
#include <string>

#include <openssl/sha.h>

namespace mastercore
{
/**
 * Obtains a hash of the active state to use for consensus verification and checkpointing.
 *
 * For increased flexibility, so other implementations like OmniWallet and OmniChest can
 * also apply this methodology without necessarily using the same exact data types (which
 * would be needed to hash the data bytes directly), create a string in the following
 * format for each entry to use for hashing:
 *
 * ---STAGE 1 - BALANCES---
 * Format specifiers & placeholders:
 *   "%s|%d|%d|%d|%d|%d" - "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve"
 *
 * Note: empty balance records and the pending tally are ignored. Addresses are sorted based
 * on lexicographical order, and balance records are sorted by the property identifiers.
 *
 * ---STAGE 2 - DEX SELL OFFERS---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d|%d|%d|%d|%d" - "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit|availableamount|acceptedamount"
 *
 * Note: ordered ascending by txid.
 *
 * ---STAGE 3 - DEX ACCEPTS---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d" - "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock"
 *
 * Note: ordered ascending by matchedselloffertxid followed by buyer.
 *
 * ---STAGE 4 - METADEX TRADES---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d|%d|%d" - "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining"
 *
 * Note: ordered ascending by txid.
 *
 * ---STAGE 5 - CROWDSALES---
 * Format specifiers & placeholders:
 *   "%d|%d|%d|%d|%d" - "propertyid|propertyiddesired|deadline|usertokens|issuertokens"
 *
 * Note: ordered by property ID.
 *
 * ---STAGE 6 - PROPERTIES---
 * Format specifiers & placeholders:
 *   "%d|%d" - "nextavailablepropertyidmaineco|nextavailablepropertyidtesteco"
 *
 * The byte order is important, and we assume:
 *   SHA256("abc") = "ad1500f261ff10b49c7a1796a36103b02322ae5dde404141eacf018fbf1678ba"
 *
 */
uint256 GetConsensusHash()
{
    // allocate and init a SHA256_CTX
    SHA256_CTX shaCtx;
    SHA256_Init(&shaCtx);

    LOCK(cs_tally);

    if (msc_debug_consensus_hash) PrintToLog("Beginning generation of current consensus hash...\n");

    // Balances - loop through the tally map, updating the sha context with the data from each balance and tally type
    // Placeholders:  "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve"
    for (std::map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it) {
        const std::string& address = my_it->first;
        CMPTally& tally = my_it->second;
        tally.init();
        uint32_t propertyId = 0;
        while (0 != (propertyId = (tally.next()))) {
            int64_t balance = tally.getMoney(propertyId, BALANCE);
            int64_t sellOfferReserve = tally.getMoney(propertyId, SELLOFFER_RESERVE);
            int64_t acceptReserve = tally.getMoney(propertyId, ACCEPT_RESERVE);
            int64_t metaDExReserve = tally.getMoney(propertyId, METADEX_RESERVE);

            // skip this entry if all balances are empty
            if (!balance && !sellOfferReserve && !acceptReserve && !metaDExReserve) continue;

            std::string dataStr = strprintf("%s|%d|%d|%d|%d|%d",
                    address, propertyId, balance, sellOfferReserve, acceptReserve, metaDExReserve);
            if (msc_debug_consensus_hash) PrintToLog("Adding balance data to consensus hash: %s\n", dataStr);
            SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
        }
    }

    // DEx sell offers - loop through the DEx and add each sell offer to the consensus hash (ordered by txid)
    // Placeholders: "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit|availableamount|acceptedamount"
    std::vector<std::pair<uint256, std::string> > vecDExOffers;
    for (OfferMap::iterator it = my_offers.begin(); it != my_offers.end(); ++it) {
        const CMPOffer& selloffer = it->second;
        const std::string& sellCombo = it->first;
        uint32_t propertyId = selloffer.getProperty();
        std::string seller = sellCombo.substr(0, sellCombo.size() - 2);
        std::string dataStr = strprintf("%s|%s|%d|%d|%d|%d|%d|%d|%d",
                selloffer.getHash().GetHex(), seller, propertyId, selloffer.getOfferAmountOriginal(),
                selloffer.getBTCDesiredOriginal(), selloffer.getMinFee(), selloffer.getBlockTimeLimit(),
                getMPbalance(seller, propertyId, SELLOFFER_RESERVE), getMPbalance(seller, propertyId, ACCEPT_RESERVE));
        vecDExOffers.push_back(std::make_pair(selloffer.getHash(), dataStr));
    }
    std::sort (vecDExOffers.begin(), vecDExOffers.end());
    for (std::vector<std::pair<uint256, std::string> >::iterator it = vecDExOffers.begin(); it != vecDExOffers.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding DEx offer data to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // DEx accepts - loop through the accepts map and add each accept to the consensus hash (ordered by matchedtxid then buyer)
    // Placeholders: "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock"
    std::vector<std::pair<std::string, std::string> > vecAccepts;
    for (AcceptMap::const_iterator it = my_accepts.begin(); it != my_accepts.end(); ++it) {
        const CMPAccept& accept = it->second;
        const std::string& acceptCombo = it->first;
        std::string buyer = acceptCombo.substr((acceptCombo.find("+") + 1), (acceptCombo.size()-(acceptCombo.find("+") + 1)));
        std::string dataStr = strprintf("%s|%s|%d|%d|%d",
                accept.getHash().GetHex(), buyer, accept.getAcceptAmount(), accept.getAcceptAmountRemaining(), accept.getAcceptBlock());
        std::string sortKey = strprintf("%s-%s", accept.getHash().GetHex(), buyer);
        vecAccepts.push_back(std::make_pair(sortKey, dataStr));
    }
    std::sort (vecAccepts.begin(), vecAccepts.end());
    for (std::vector<std::pair<std::string, std::string> >::iterator it = vecAccepts.begin(); it != vecAccepts.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding DEx accept to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // MetaDEx trades - loop through the MetaDEx maps and add each open trade to the consensus hash (ordered by txid)
    // Placeholders: "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining"
    std::vector<std::pair<uint256, std::string> > vecMetaDExTrades;
    for (md_PropertiesMap::const_iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it) {
        const md_PricesMap& prices = my_it->second;
        for (md_PricesMap::const_iterator it = prices.begin(); it != prices.end(); ++it) {
            const md_Set& indexes = it->second;
            for (md_Set::const_iterator it = indexes.begin(); it != indexes.end(); ++it) {
                const CMPMetaDEx& obj = *it;
                std::string dataStr = strprintf("%s|%s|%d|%d|%d|%d|%d",
                    obj.getHash().GetHex(), obj.getAddr(), obj.getProperty(), obj.getAmountForSale(),
                    obj.getDesProperty(), obj.getAmountDesired(), obj.getAmountRemaining());
                    vecMetaDExTrades.push_back(std::make_pair(obj.getHash(), dataStr));
            }
        }
    }
    std::sort (vecMetaDExTrades.begin(), vecMetaDExTrades.end());
    for (std::vector<std::pair<uint256, std::string> >::iterator it = vecMetaDExTrades.begin(); it != vecMetaDExTrades.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding MetaDEx trade data to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // Crowdsales - loop through open crowdsales and add to the consensus hash (ordered by property ID)
    // Note: the variables of the crowdsale (amount, bonus etc) are not part of the crowdsale map and not included here to
    // avoid additionalal loading of SP entries from the database
    // Placeholders: "propertyid|propertyiddesired|deadline|usertokens|issuertokens"
    std::vector<std::pair<uint32_t, std::string> > vecCrowds;
    for (CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it) {
        const CMPCrowd& crowd = it->second;
        uint32_t propertyId = crowd.getPropertyId();
        std::string dataStr = strprintf("%d|%d|%d|%d|%d",
            crowd.getPropertyId(), crowd.getCurrDes(), crowd.getDeadline(), crowd.getUserCreated(), crowd.getIssuerCreated());
        vecCrowds.push_back(std::make_pair(propertyId, dataStr));
    }
    std::sort (vecCrowds.begin(), vecCrowds.end());
    for (std::vector<std::pair<uint32_t, std::string> >::iterator it = vecCrowds.begin(); it != vecCrowds.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding Crowdsale entry to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // Properties - add the next available property ID in both the main and test ecosystems
    // Placeholders: "nextavailablepropertyidmaineco|nextavailablepropertyidtesteco"
    std::string dataStr = strprintf("%d|%d", _my_sps->peekNextSPID(1), _my_sps->peekNextSPID(2));
    if (msc_debug_consensus_hash) PrintToLog("Adding property to consensus hash: %s\n", dataStr);
    SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());

    // extract the final result and return the hash
    uint256 consensusHash;
    SHA256_Final((unsigned char*)&consensusHash, &shaCtx);
    if (msc_debug_consensus_hash) PrintToLog("Finished generation of consensus hash.  Result: %s\n", consensusHash.GetHex());

    return consensusHash;
}

} // namespace mastercore
<|endoftext|>


<commit_before>#pragma once
#include "../cni.hpp"
static cs::extension hash_map_ext;
static cs::extension_t hash_map_ext_shared=std::make_shared<cs::extension_holder>(&hash_map_ext);
namespace cs_any {
	template<>cs::extension_t& get_ext<cs::hash_map>()
	{
		return hash_map_ext_shared;
	}
}
namespace hash_map_cs_ext {
	using namespace cs;
	void clear(hash_map& map)
	{
		map.clear();
	}
	void insert(hash_map& map,const cs::any& key,const cs::any& val)
	{
		if(map.count(key)>0)
			map.at(key).swap(copy(val),true);
		else
			map.emplace(copy(key),copy(val));
	}
	void erase(hash_map& map,const cs::any& key)
	{
		map.erase(key);
	}
	bool exist(hash_map& map,const cs::any& key)
	{
		return map.count(key)>0;
	}
	cs::any at(hash_map& map,const cs::any& key)
	{
		return map.at(key);
	}
	number size(const hash_map& map)
	{
		return map.size();
	}
	void init()
	{
		hash_map_ext.add_var("clear",cs::any::make_protect<native_interface>(cni(clear),true));
		hash_map_ext.add_var("insert",cs::any::make_protect<native_interface>(cni(insert),true));
		hash_map_ext.add_var("erase",cs::any::make_protect<native_interface>(cni(erase),true));
		hash_map_ext.add_var("exist",cs::any::make_protect<native_interface>(cni(exist),true));
		hash_map_ext.add_var("at",cs::any::make_protect<native_interface>(cni(at),true));
		hash_map_ext.add_var("size",cs::any::make_protect<native_interface>(cni(size),true));
	}
}
<commit_msg>完善哈希表的功能<commit_after>#pragma once
#include "../cni.hpp"
static cs::extension hash_map_ext;
static cs::extension_t hash_map_ext_shared=std::make_shared<cs::extension_holder>(&hash_map_ext);
namespace cs_any {
	template<>cs::extension_t& get_ext<cs::hash_map>()
	{
		return hash_map_ext_shared;
	}
}
namespace hash_map_cs_ext {
	using namespace cs;
// Capacity
	bool empty(const hash_map& map)
	{
		return map.empty();
	}
	number size(const hash_map& map)
	{
		return map.size();
	}
// Modifiers
	void clear(hash_map& map)
	{
		map.clear();
	}
	void insert(hash_map& map,const cs::any& key,const cs::any& val)
	{
		if(map.count(key)>0)
			map.at(key).swap(copy(val),true);
		else
			map.emplace(copy(key),copy(val));
	}
	void erase(hash_map& map,const cs::any& key)
	{
		map.erase(key);
	}
// Lookup
	cs::any at(hash_map& map,const cs::any& key)
	{
		return map.at(key);
	}
	bool exist(hash_map& map,const cs::any& key)
	{
		return map.count(key)>0;
	}
	void init()
	{
		hash_map_ext.add_var("empty",cs::any::make_protect<native_interface>(cni(empty),true));
		hash_map_ext.add_var("size",cs::any::make_protect<native_interface>(cni(size),true));
		hash_map_ext.add_var("clear",cs::any::make_protect<native_interface>(cni(clear),true));
		hash_map_ext.add_var("insert",cs::any::make_protect<native_interface>(cni(insert),true));
		hash_map_ext.add_var("erase",cs::any::make_protect<native_interface>(cni(erase),true));
		hash_map_ext.add_var("at",cs::any::make_protect<native_interface>(cni(at),true));
		hash_map_ext.add_var("exist",cs::any::make_protect<native_interface>(cni(exist),true));
	}
}
<|endoftext|>


<commit_before>//
//  LogHandler.cpp
//  Audiologger
//
//  Created by Malte Poll on 14.02.16.
//  Copyright © 2016 Malte Poll. All rights reserved.
//

#include "LogHandler.hpp"
#include "dirent.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <algorithm>

char* create_c_string(std::string in){
    std::string completeFileName = in;
    const char* temp = in.c_str();
    char* out = new char[in.length()];
    strcpy(out, temp);
    return out;
}

inline bool isInteger(const std::string & s)
{
    if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;

    char * p ;
    strtol(s.c_str(), &p, 10) ;

    return (*p == 0) ;
}

std::string getFileExtension(const std::string& FileName)
{
    if(FileName.find_last_of(".") != std::string::npos)
        return FileName.substr(FileName.find_last_of(".")+1);
    return "";
}

void LogHandler::listAllFileNames(){
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(getFolderPath().c_str())) != NULL) {
        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
            if(*new std::string(getFileExtension(ent->d_name)) == "wav"){
                std::cout << ent->d_name << std::endl;
            }
        }
        closedir (dir);
    }
}

void LogHandler::listAllLogNames(){
    for (int i=0; i<logList.getNumObjects(); i++) {
        formatted_duration_t dur = logList.getVarAt(i)->getFormattedDuration();
        std::cout << i+1 << " " << logList.getVarAt(i)->getTitle() << "\t|\t " << dur.hours<<":"<<dur.minutes<<":"<<dur.seconds << std::endl;
    }
}

void LogHandler::init(){
    rec = new Recorder();
    player = new Player();
    readAudioLogsFromDisk();
}

LogHandler::LogHandler(){
    init();
}

LogHandler::LogHandler(std::string folderPath){
    setFolderPath(folderPath);
    init();
}

LogHandler::~LogHandler(){
    delete rec;
    logList.clear();
}

int LogHandler::audioLogsOnDisk(){
    int amount = 0;
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(getFolderPath().c_str())) != NULL) {
        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
            if(*new std::string(getFileExtension(ent->d_name)) == "wav"){
                amount++;
            }
        }
        closedir (dir);
    }
    return amount;
}

void LogHandler::readAudioLogsFromDisk(){
    int i = 0;
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(getFolderPath().c_str())) != NULL) {
        while ((ent = readdir (dir)) != NULL) {
            if(*new std::string(getFileExtension(ent->d_name)) == "wav"){
                logList.push(new Audiolog("", getFolderPath()));
                logList.fst()->setFileName(ent->d_name);
                logList.fst()->retrieveFileInfo();
                i++;
            }
        }
        closedir (dir);
    }
}

void LogHandler::displayPlaybackInfo(){
    if (!player->isPlaying()) {
        std::cout << "Noting is being played right now. You can play a log by typing 'p'" << std::endl;
    }else{
        std::cout << "Now playing \""<<currentlyPlayed->getTitle()<<"\"\nPlayed "<<100*player->getTimePlayed()/player->getDuration()<<"%\n";
    }
}

int LogHandler::deleteLog(){
    std::cout << "Wich log should be deleted?" << std::endl;
    listAllLogNames();
    std::cout << "Please enter the number of the log or type q to quit" << std::endl;
    std::string str_index;
    std::cin >> str_index;
    if (str_index == "q" || str_index == "Q") {
        std::cout << "Will not delete anything!" << std::endl;
        return -1;
    }
    if (!isInteger(str_index)) {
        std::cout << "You did not enter a valid number." << std::endl;
        return -1;
    }
    int index = std::stoi(str_index)-1;

    if (index >= logList.getNumObjects() || index < 0) {
        std::cout << "There is no log with this number" << std::endl;
        return -1;
    }

    if (!logList.getVarAt(index)->getFileExists()) {
        std::cout << "Audio file does not exist. Cannot be deleted." << std::endl;
        return -1;
    }

    std::cout << "You are about to delete the log '"<<logList.getVarAt(index)->getTitle()<<"'. Are you sure? (Type 'y' or 'Y' to confirm)" << std::endl;
    std::string confirmation;
    std::cin >> confirmation;
    if (confirmation != "y" && confirmation != "Y") {
        std::cout << "Will not delete the log." << std::endl;
        return -1;
    }
    return deleteLog(index);
}
int LogHandler::deleteLog(int index){
    if (logList.getVarAt(index)->getFileExists()) {
        int res = remove((logList.getVarAt(index)->getFilePath()+logList.getVarAt(index)->getFileName()).c_str());
        if (res == 0) {
            logList.deleteAt(index);
            return 0;
        }
    }
    return -1;
}

int LogHandler::recordLog(){
    std::string titleForNewLog = "";
    getline(std::cin, titleForNewLog);
    while(titleForNewLog.empty()){
        std::cout << "What title should the new audiolog have?: ";
        getline(std::cin, titleForNewLog);
        std::cout << std::endl;
    }
    Audiolog* newLog = new Audiolog(titleForNewLog, folderPath);
    return recordLog(newLog);
}

int LogHandler::recordLog(Audiolog* log){
    std::string completeFileName = log->getFilePath()+log->getFileName();
    rec->setDestPath(completeFileName.c_str());
    std::cout << "The recording of " << log->getTitle() << " starts when you press ENTER" <<std::endl;
    std::string userinput;
    getline(std::cin, userinput);

    rec->startRecording();
    std::cout << "Stop by pressing ENTER" << std::endl;
    getline(std::cin, userinput);
    rec->stopRecording();
    log->writeFileInfo();
    logList.push(log);
    return 0;
}

int LogHandler::playLog(){
    std::string userinput;
    int indexOfLog = 0;
    listAllLogNames();
    std::cout << "What audiolog should be played?: ";
    //while (getchar()!='\n') {}
    getline(std::cin, userinput);
    if(!isInteger(userinput)){
        std::cout << "Please enter a number" <<std::endl;
        return -1;
    }
    indexOfLog = stoi(userinput);
    std::cout << std::endl;
    indexOfLog--;
    if (indexOfLog >= logList.getNumObjects() || indexOfLog < 0) {
        std::cout << "Not a valid log number." <<std::endl;
        return -1;
    }
    return playLog(logList.getVarAt(indexOfLog));
}

int LogHandler::playLog(Audiolog *log){
    std::string completeFileName = log->getFilePath()+log->getFileName();
    player->setDestPath(completeFileName.c_str());
    currentlyPlayed = log;

    //char userinput;
    player->setDuration(log->getDuration());
    player->startPlaying();
    return 0;
}

int LogHandler::stopLog(){
    if(player->isPlaying()){
        player->stopPlaying();
        currentlyPlayed = nullptr;
        return 0;
    }
    return -1;
}

Audiolog* LogHandler::getAudiologByIndex(int index){
    return logList.getVarAt(index);
}

void LogHandler::setFolderPath(std::string folderPath){
    this->folderPath = folderPath;
}

std::string LogHandler::getFolderPath(){
    return folderPath;
}

<commit_msg>Update LogHandler.cpp<commit_after>//
//  LogHandler.cpp
//  Audiologger
//
//  Created by Malte Poll on 14.02.16.
//  Copyright © 2016 Malte Poll. All rights reserved.
//

#include "LogHandler.hpp"
#include "dirent.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <cstring>
#include <algorithm>

char* create_c_string(std::string in){
    std::string completeFileName = in;
    const char* temp = in.c_str();
    char* out = new char[in.length()];
    strcpy(out, temp);
    return out;
}

inline bool isInteger(const std::string & s)
{
    if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;

    char * p ;
    strtol(s.c_str(), &p, 10) ;

    return (*p == 0) ;
}

std::string getFileExtension(const std::string& FileName)
{
    if(FileName.find_last_of(".") != std::string::npos)
        return FileName.substr(FileName.find_last_of(".")+1);
    return "";
}

void LogHandler::listAllFileNames(){
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(getFolderPath().c_str())) != NULL) {
        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
            if(*new std::string(getFileExtension(ent->d_name)) == "wav"){
                std::cout << ent->d_name << std::endl;
            }
        }
        closedir (dir);
    }
}

void LogHandler::listAllLogNames(){
    for (int i=0; i<logList.getNumObjects(); i++) {
        formatted_duration_t dur = logList.getVarAt(i)->getFormattedDuration();
        std::cout << i+1 << " " << logList.getVarAt(i)->getTitle() << "\t|\t " << dur.hours<<":"<<dur.minutes<<":"<<dur.seconds << std::endl;
    }
}

void LogHandler::init(){
    rec = new Recorder();
    player = new Player();
    readAudioLogsFromDisk();
}

LogHandler::LogHandler(){
    init();
}

LogHandler::LogHandler(std::string folderPath){
    setFolderPath(folderPath);
    init();
}

LogHandler::~LogHandler(){
    delete rec;
    logList.clear();
}

int LogHandler::audioLogsOnDisk(){
    int amount = 0;
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(getFolderPath().c_str())) != NULL) {
        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
            if(*new std::string(getFileExtension(ent->d_name)) == "wav"){
                amount++;
            }
        }
        closedir (dir);
    }
    return amount;
}

void LogHandler::readAudioLogsFromDisk(){
    int i = 0;
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(getFolderPath().c_str())) != NULL) {
        while ((ent = readdir (dir)) != NULL) {
            if(*new std::string(getFileExtension(ent->d_name)) == "wav"){
                logList.push(new Audiolog("", getFolderPath()));
                logList.fst()->setFileName(ent->d_name);
                logList.fst()->retrieveFileInfo();
                i++;
            }
        }
        closedir (dir);
    }
}

void LogHandler::displayPlaybackInfo(){
    if (!player->isPlaying()) {
        std::cout << "Noting is being played right now. You can play a log by typing 'p'" << std::endl;
    }else{
        std::cout << "Now playing \""<<currentlyPlayed->getTitle()<<"\"\nPlayed "<<100*player->getTimePlayed()/player->getDuration()<<"%\n";
    }
}

int LogHandler::deleteLog(){
    std::cout << "Wich log should be deleted?" << std::endl;
    listAllLogNames();
    std::cout << "Please enter the number of the log or type q to quit" << std::endl;
    std::string str_index;
    std::cin >> str_index;
    if (str_index == "q" || str_index == "Q") {
        std::cout << "Will not delete anything!" << std::endl;
        return -1;
    }
    if (!isInteger(str_index)) {
        std::cout << "You did not enter a valid number." << std::endl;
        return -1;
    }
    int index = std::stoi(str_index)-1;

    if (index >= logList.getNumObjects() || index < 0) {
        std::cout << "There is no log with this number" << std::endl;
        return -1;
    }

    if (!logList.getVarAt(index)->getFileExists()) {
        std::cout << "Audio file does not exist. Cannot be deleted." << std::endl;
        return -1;
    }

    std::cout << "You are about to delete the log '"<<logList.getVarAt(index)->getTitle()<<"'. Are you sure? (Type 'y' or 'Y' to confirm)" << std::endl;
    std::string confirmation;
    std::cin >> confirmation;
    if (confirmation != "y" && confirmation != "Y") {
        std::cout << "Will not delete the log." << std::endl;
        return -1;
    }
    return deleteLog(index);
}
int LogHandler::deleteLog(int index){
    if (logList.getVarAt(index)->getFileExists()) {
        int res = remove((logList.getVarAt(index)->getFilePath()+logList.getVarAt(index)->getFileName()).c_str());
        if (res == 0) {
            logList.deleteAt(index);
            return 0;
        }
    }
    return -1;
}

int LogHandler::recordLog(){
    std::string titleForNewLog = "";
    getline(std::cin, titleForNewLog);
    while(titleForNewLog.empty()){
        std::cout << "What title should the new audiolog have?: ";
        getline(std::cin, titleForNewLog);
        std::cout << std::endl;
    }
    Audiolog* newLog = new Audiolog(titleForNewLog, folderPath);
    return recordLog(newLog);
}

int LogHandler::recordLog(Audiolog* log){
    std::string completeFileName = log->getFilePath()+log->getFileName();
    rec->setDestPath(completeFileName.c_str());
    std::cout << "The recording of " << log->getTitle() << " starts when you press ENTER" <<std::endl;
    std::string userinput;
    getline(std::cin, userinput);

    rec->startRecording();
    std::cout << "Stop by pressing ENTER" << std::endl;
    getline(std::cin, userinput);
    rec->stopRecording();
    log->writeFileInfo();
    logList.push(log);
    return 0;
}

int LogHandler::playLog(){
    std::string userinput;
    int indexOfLog = 0;
    listAllLogNames();
    std::cout << "What audiolog should be played?: ";
    //while (getchar()!='\n') {}
    getline(std::cin, userinput);
    if(!isInteger(userinput)){
        std::cout << "Please enter a number" <<std::endl;
        return -1;
    }
    indexOfLog = stoi(userinput);
    std::cout << std::endl;
    indexOfLog--;
    if (indexOfLog >= logList.getNumObjects() || indexOfLog < 0) {
        std::cout << "Not a valid log number." <<std::endl;
        return -1;
    }
    return playLog(logList.getVarAt(indexOfLog));
}

int LogHandler::playLog(Audiolog *log){
    std::string completeFileName = log->getFilePath()+log->getFileName();
    player->setDestPath(completeFileName.c_str());
    currentlyPlayed = log;

    //char userinput;
    player->setDuration(log->getDuration());
    player->startPlaying();
    return 0;
}

int LogHandler::stopLog(){
    if(player->isPlaying()){
        player->stopPlaying();
        currentlyPlayed = nullptr;
        return 0;
    }
    return -1;
}

Audiolog* LogHandler::getAudiologByIndex(int index){
    return logList.getVarAt(index);
}

void LogHandler::setFolderPath(std::string folderPath){
    this->folderPath = folderPath;
}

std::string LogHandler::getFolderPath(){
    return folderPath;
}

<|endoftext|>