<commit_before>
#include "../Flare.h"
#include "FlareSoundManager.h"
#include "FlarePlayerController.h"
#include "../Spacecrafts/FlareSpacecraft.h"
#include "../Spacecrafts/FlareOrbitalEngine.h"
#include "AudioDevice.h"

#define LOCTEXT_NAMESPACE "UFlareSoundManager"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareSoundManager::UFlareSoundManager(const class FObjectInitializer& PCIP)
	: Super(PCIP)
{
	// Gameplay data
	ShipPawn = NULL;
	MasterVolume = 1.0f;
	MusicVolume = 1.0f;
	EffectsVolume = 1.0f;
	MusicChanging = false;
	MusicDesiredTrack = EFlareMusicTrack::None;

	// Music track references
	static ConstructorHelpers::FObjectFinder<USoundCue> MenuMusicObj(TEXT("/Game/Music/A_Menu_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> ExplorationMusicObj(TEXT("/Game/Music/A_Exploration_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> TravelMusicObj(TEXT("/Game/Music/A_Exploration_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> DangerMusicObk(TEXT("/Game/Music/A_Danger_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> PacificMusicObj(TEXT("/Game/Music/A_Exploration_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> CombatMusicObj(TEXT("/Game/Music/A_Combat_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> WarMusicObj(TEXT("/Game/Music/A_Combat_Cue"));

	// Mix references
	static ConstructorHelpers::FObjectFinder<USoundClass> MasterClassObj(TEXT("/Engine/EngineSounds/Master"));
	static ConstructorHelpers::FObjectFinder<USoundClass> MusicClassObj(TEXT("/Engine/EngineSounds/Music"));
	static ConstructorHelpers::FObjectFinder<USoundClass> EffectsClassObj(TEXT("/Engine/EngineSounds/SFX"));
	static ConstructorHelpers::FObjectFinder<USoundMix> MasterSoundMixObj(TEXT("/Game/Sound/Mix_Master"));

	// Sound references
	static ConstructorHelpers::FObjectFinder<USoundCue> TargetWarningSoundObj(TEXT("/Game/Sound/Game/A_TargetWarning"));
	static ConstructorHelpers::FObjectFinder<USoundCue> AttackWarningSoundObj(TEXT("/Game/Sound/Game/A_AttackWarning"));
	static ConstructorHelpers::FObjectFinder<USoundCue> HealthWarningSoundObj(TEXT("/Game/Sound/Game/A_HealthWarning"));
	static ConstructorHelpers::FObjectFinder<USoundCue> HealthWarningHeavySoundObj(TEXT("/Game/Sound/Game/A_HealthWarningHeavy"));

	// Sound class
	MasterSoundClass = MasterClassObj.Object;
	MusicSoundClass = MusicClassObj.Object;
	EffectsSoundClass = EffectsClassObj.Object;
	MasterSoundMix = MasterSoundMixObj.Object;

	// Music track store
	MusicTracks.Add(NULL);
	MusicTracks.Add(MenuMusicObj.Object);
	MusicTracks.Add(ExplorationMusicObj.Object);
	MusicTracks.Add(TravelMusicObj.Object);
	MusicTracks.Add(PacificMusicObj.Object);
	MusicTracks.Add(DangerMusicObk.Object);
	MusicTracks.Add(CombatMusicObj.Object);
	MusicTracks.Add(WarMusicObj.Object);

	// Sound references
	TargetWarningSound = TargetWarningSoundObj.Object;
	AttackWarningSound = AttackWarningSoundObj.Object;
	HealthWarningSound = HealthWarningSoundObj.Object;
	HealthWarningHeavySound = HealthWarningHeavySoundObj.Object;

	// Music sound
	MusicPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("MusicSound"));
	MusicPlayer.Sound->bAutoActivate = false;
	MusicPlayer.Sound->bAutoDestroy = false;
	MusicPlayer.PitchedFade = false;
	MusicPlayer.FadeSpeed = 0.2;
	MusicPlayer.Volume = 0;

	// Power sound
	PowerPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("PowerSound"));
	PowerPlayer.Sound->bAutoActivate = false;
	PowerPlayer.Sound->bAutoDestroy = false;
	PowerPlayer.PitchedFade = true;
	PowerPlayer.FadeSpeed = 0.3;
	PowerPlayer.Volume = 0;

	// Engine sound
	EnginePlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("EngineSound"));
	EnginePlayer.Sound->bAutoActivate = false;
	EnginePlayer.Sound->bAutoDestroy = false;
	EnginePlayer.PitchedFade = true;
	EnginePlayer.FadeSpeed = 1.0;
	EnginePlayer.Volume = 0;

	// RCS sound
	RCSPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("RCSSound"));
	RCSPlayer.Sound->bAutoActivate = false;
	RCSPlayer.Sound->bAutoDestroy = false;
	RCSPlayer.PitchedFade = true;
	RCSPlayer.FadeSpeed = 2.0;
	RCSPlayer.Volume = 0;

	// Warning sound on targeting
	TargetWarningPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("TargetWarningSound"));
	TargetWarningPlayer.Sound->bAutoActivate = false;
	TargetWarningPlayer.Sound->bAutoDestroy = false;
	TargetWarningPlayer.PitchedFade = false;
	TargetWarningPlayer.FadeSpeed = 10.0f;
	TargetWarningPlayer.Volume = 0;

	// Warning sound on attack
	AttackWarningPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("AttackWarningSound"));
	AttackWarningPlayer.Sound->bAutoActivate = false;
	AttackWarningPlayer.Sound->bAutoDestroy = false;
	AttackWarningPlayer.PitchedFade = false;
	AttackWarningPlayer.FadeSpeed = 10.0f;
	AttackWarningPlayer.Volume = 0;

	// Warning sound on low health
	HealthWarningPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("HealthWarningSound"));
	HealthWarningPlayer.Sound->bAutoActivate = false;
	HealthWarningPlayer.Sound->bAutoDestroy = false;
	HealthWarningPlayer.PitchedFade = false;
	HealthWarningPlayer.FadeSpeed = 1.0f;
	HealthWarningPlayer.Volume = 0;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

void UFlareSoundManager::Setup(AFlarePlayerController* Player)
{
	FLOG("UFlareSoundManager::Setup");
	PC = Player;

	// Attach all sounds
	if (Player)
	{
		USceneComponent* RootComponent = Player->GetRootComponent();
		FAttachmentTransformRules AttachRules(EAttachmentRule::KeepWorld, true);

		// Music
		MusicPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		MusicPlayer.Sound->SetSound(NULL);

		// Engine sounds
		PowerPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		EnginePlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		RCSPlayer.Sound->AttachToComponent(RootComponent, AttachRules);

		// Warning sounds
		TargetWarningPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		AttackWarningPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		HealthWarningPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		TargetWarningPlayer.Sound->SetSound(TargetWarningSound);
		AttackWarningPlayer.Sound->SetSound(AttackWarningSound);
		HealthWarningPlayer.Sound->SetSound(HealthWarningSound);

		// Device
		FAudioDevice* AudioDevice = Player->GetWorld()->GetAudioDevice();
		FCHECK(AudioDevice);
		FCHECK(MasterSoundClass);
		FCHECK(MasterSoundMix);
		AudioDevice->SetBaseSoundMix(MasterSoundMix);
	}
}

void UFlareSoundManager::SetMusicVolume(int32 Volume)
{
	FLOGV("UFlareSoundManager::SetMusicVolume %d", Volume);

	MusicVolume = FMath::Clamp(Volume / 10.0f, 0.0f, 1.0f);

	// Update
	FAudioDevice* AudioDevice = PC->GetWorld()->GetAudioDevice();
	FCHECK(AudioDevice);
	AudioDevice->SetSoundMixClassOverride(MasterSoundMix, MusicSoundClass, MusicVolume, 1.0f, 0.5f, true);
}

void UFlareSoundManager::SetMasterVolume(int32 Volume)
{
	FLOGV("UFlareSoundManager::SetMasterVolume %d", Volume);

	MasterVolume = FMath::Clamp(Volume / 10.0f, 0.0f, 1.0f);

	// Update
	FAudioDevice* AudioDevice = PC->GetWorld()->GetAudioDevice();
	FCHECK(AudioDevice);
	AudioDevice->SetSoundMixClassOverride(MasterSoundMix, MasterSoundClass, MasterVolume, 1.0f, 0.5f, true);
}

void UFlareSoundManager::Update(float DeltaSeconds)
{
	// Update the ship if necessary
	AFlareSpacecraft* PlayerShip = PC->GetShipPawn();
	if (PlayerShip != ShipPawn)
	{
		SetCurrentSpacecraft(PlayerShip);
	}

	// Music management
	if (MusicChanging)
	{
		if (MusicPlayer.Volume > 0)
		{
			UpdatePlayer(MusicPlayer, -DeltaSeconds);
		}
		else
		{
			SetDesiredMusicTrack();
		}
	}
	else if (MusicDesiredTrack == EFlareMusicTrack::None)
	{
		UpdatePlayer(MusicPlayer, -DeltaSeconds);
	}
	else
	{
		UpdatePlayer(MusicPlayer, +DeltaSeconds);
	}

	// Has a ship : update ship sounds
	if (ShipPawn)
	{
		// Engine values
		float RCSAlpha = 0;
		float EngineAlpha = 0;
		int32 RCSCount = 0;
		int32 EngineCount = 0;

		// Check all engines for engine alpha values
		TArray<UActorComponent*> Engines = ShipPawn->GetComponentsByClass(UFlareEngine::StaticClass());
		for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++)
		{
			UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]);
			if (Engine->IsA(UFlareOrbitalEngine::StaticClass()))
			{
				EngineAlpha += Engine->GetEffectiveAlpha();
				EngineCount++;
			}
			else
			{
				RCSAlpha += Engine->GetEffectiveAlpha();
				RCSCount++;
			}
		}

		// Get player threats
		bool Targeted, FiredUpon;
		UFlareSimulatedSpacecraft* Threat;
		PC->GetPlayerShipThreatStatus(Targeted, FiredUpon, Threat);
		UFlareSimulatedSpacecraftDamageSystem* DamageSystem = PlayerShip->GetParent()->GetDamageSystem();
		bool PlayerShipEndangered = (DamageSystem->IsCrewEndangered() || DamageSystem->IsUncontrollable()) && DamageSystem->IsAlive();

		// Update engine sounds
		UpdatePlayer(PowerPlayer,  (!DamageSystem->IsUncontrollable() && !DamageSystem->HasPowerOutage() ? 1 : -1) * DeltaSeconds);
		UpdatePlayer(EnginePlayer, (EngineAlpha > 0 ? EngineAlpha / EngineCount : -1) * DeltaSeconds);
		UpdatePlayer(RCSPlayer,    (RCSAlpha > 0 ? RCSAlpha / RCSCount : -1)          * DeltaSeconds);

		// Update alarms
		bool IsHeavy = (ShipPawn->GetParent()->GetDescription()->Size == EFlarePartSize::L);
		UpdatePlayer(TargetWarningPlayer, (!IsHeavy && !FiredUpon & Targeted ?  1.0f : -1.0f) * DeltaSeconds);
		UpdatePlayer(AttackWarningPlayer, (!IsHeavy &&  FiredUpon ?             1.0f : -1.0f) * DeltaSeconds);
		UpdatePlayer(HealthWarningPlayer, (PlayerShipEndangered && !FiredUpon ? 1.0f : -1.0f) * DeltaSeconds);
	}

	// No ship : stop all ship sounds
	else
	{
		UpdatePlayer(PowerPlayer,         -DeltaSeconds);
		UpdatePlayer(EnginePlayer,        -DeltaSeconds);
		UpdatePlayer(RCSPlayer,           -DeltaSeconds);
		UpdatePlayer(TargetWarningPlayer, -DeltaSeconds);
		UpdatePlayer(AttackWarningPlayer, -DeltaSeconds);
		UpdatePlayer(HealthWarningPlayer, -DeltaSeconds);
	}

	UpdateEffectsVolume(DeltaSeconds);
}

void UFlareSoundManager::RequestMusicTrack(EFlareMusicTrack::Type NewTrack)
{
	FLOGV("UFlareSoundManager::RequestMusicTrack : requested %d", (int32)(NewTrack - EFlareMusicTrack::None));
	if (NewTrack != MusicDesiredTrack)
	{
		FLOG("UFlareSoundManager::RequestMusicTrack : switching");
		MusicDesiredTrack = NewTrack;
		MusicChanging = true;
	}
}

/*----------------------------------------------------
	Internal methods
----------------------------------------------------*/

void UFlareSoundManager::SetCurrentSpacecraft(AFlareSpacecraft* Ship)
{
	ShipPawn = Ship;

	if (Ship)
	{
		// Setup power sound
		FFlareSpacecraftDescription* ShipDescription = Ship->GetParent()->GetDescription();
		PowerPlayer.Sound->SetSound(ShipDescription ? ShipDescription->PowerSound : NULL);

		// Setup orbital engine sound
		FFlareSpacecraftComponentDescription* EngineDescription = Ship->GetOrbitalEngineDescription();
		EnginePlayer.Sound->SetSound(EngineDescription ? EngineDescription->EngineCharacteristics.EngineSound : NULL);

		// Setup RCS sound
		FFlareSpacecraftComponentDescription* RCSDescription = Ship->GetRCSDescription();
		RCSPlayer.Sound->SetSound(RCSDescription ? RCSDescription->EngineCharacteristics.EngineSound : NULL);

		// Setup warning sound
		HealthWarningPlayer.Sound->Sound = ShipDescription->Size == EFlarePartSize::L ? HealthWarningHeavySound : HealthWarningSound;
	}
}

void UFlareSoundManager::SetDesiredMusicTrack()
{
	int32 TrackIndex = (int32)(MusicDesiredTrack - EFlareMusicTrack::None);
	FLOGV("UFlareSoundManager::SetDesiredMusicTrack : starting %d", TrackIndex);
	FCHECK(TrackIndex >= 0 && TrackIndex < MusicTracks.Num());

	MusicPlayer.Sound->SetSound(MusicTracks[TrackIndex]);
	MusicChanging = false;
}

void UFlareSoundManager::UpdateEffectsVolume(float DeltaSeconds)
{
	// Fade in menu transitions
	if (PC)
	{
		if (PC->GetMenuManager()->IsFading())
		{
			EffectsVolume -= DeltaSeconds / PC->GetMenuManager()->GetFadeDuration();
		}
		else
		{
			EffectsVolume += DeltaSeconds / PC->GetMenuManager()->GetFadeDuration();
		}
		EffectsVolume = FMath::Clamp(EffectsVolume, 0.01f, 1.0f);
	}

	// Get device and update
	FAudioDevice* AudioDevice = PC->GetWorld()->GetAudioDevice();
	FCHECK(AudioDevice);
	AudioDevice->SetSoundMixClassOverride(MasterSoundMix, EffectsSoundClass, EffectsVolume, 1.0f, 0.0f, true);
}

void UFlareSoundManager::UpdatePlayer(FFlareSoundPlayer& Player, float VolumeDelta)
{
	float NewVolume = FMath::Clamp(Player.Volume + VolumeDelta * Player.FadeSpeed, 0.0f, 1.0f);

	if (NewVolume != Player.Volume)
	{
		if (NewVolume == 0)
		{
			Player.Sound->Stop();
		}
		else if (Player.Volume == 0)
		{
			Player.Sound->Play();
		}
		else
		{
			Player.Sound->SetVolumeMultiplier(NewVolume);
			Player.Sound->SetPitchMultiplier(Player.PitchedFade ? 0.5f + 0.5f * NewVolume : 1.0f);
		}
		Player.Volume = NewVolume;
	}
}

void UFlareSoundManager::StopPlayer(FFlareSoundPlayer& Player)
{
	Player.Sound->Stop();
	Player.Volume = 0;
}

#undef LOCTEXT_NAMESPACE
<commit_msg>Always allow health alarm to be played<commit_after>
#include "../Flare.h"
#include "FlareSoundManager.h"
#include "FlarePlayerController.h"
#include "../Spacecrafts/FlareSpacecraft.h"
#include "../Spacecrafts/FlareOrbitalEngine.h"
#include "AudioDevice.h"

#define LOCTEXT_NAMESPACE "UFlareSoundManager"

/*----------------------------------------------------
	Constructor
----------------------------------------------------*/

UFlareSoundManager::UFlareSoundManager(const class FObjectInitializer& PCIP)
	: Super(PCIP)
{
	// Gameplay data
	ShipPawn = NULL;
	MasterVolume = 1.0f;
	MusicVolume = 1.0f;
	EffectsVolume = 1.0f;
	MusicChanging = false;
	MusicDesiredTrack = EFlareMusicTrack::None;

	// Music track references
	static ConstructorHelpers::FObjectFinder<USoundCue> MenuMusicObj(TEXT("/Game/Music/A_Menu_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> ExplorationMusicObj(TEXT("/Game/Music/A_Exploration_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> TravelMusicObj(TEXT("/Game/Music/A_Exploration_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> DangerMusicObk(TEXT("/Game/Music/A_Danger_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> PacificMusicObj(TEXT("/Game/Music/A_Exploration_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> CombatMusicObj(TEXT("/Game/Music/A_Combat_Cue"));
	static ConstructorHelpers::FObjectFinder<USoundCue> WarMusicObj(TEXT("/Game/Music/A_Combat_Cue"));

	// Mix references
	static ConstructorHelpers::FObjectFinder<USoundClass> MasterClassObj(TEXT("/Engine/EngineSounds/Master"));
	static ConstructorHelpers::FObjectFinder<USoundClass> MusicClassObj(TEXT("/Engine/EngineSounds/Music"));
	static ConstructorHelpers::FObjectFinder<USoundClass> EffectsClassObj(TEXT("/Engine/EngineSounds/SFX"));
	static ConstructorHelpers::FObjectFinder<USoundMix> MasterSoundMixObj(TEXT("/Game/Sound/Mix_Master"));

	// Sound references
	static ConstructorHelpers::FObjectFinder<USoundCue> TargetWarningSoundObj(TEXT("/Game/Sound/Game/A_TargetWarning"));
	static ConstructorHelpers::FObjectFinder<USoundCue> AttackWarningSoundObj(TEXT("/Game/Sound/Game/A_AttackWarning"));
	static ConstructorHelpers::FObjectFinder<USoundCue> HealthWarningSoundObj(TEXT("/Game/Sound/Game/A_HealthWarning"));
	static ConstructorHelpers::FObjectFinder<USoundCue> HealthWarningHeavySoundObj(TEXT("/Game/Sound/Game/A_HealthWarningHeavy"));

	// Sound class
	MasterSoundClass = MasterClassObj.Object;
	MusicSoundClass = MusicClassObj.Object;
	EffectsSoundClass = EffectsClassObj.Object;
	MasterSoundMix = MasterSoundMixObj.Object;

	// Music track store
	MusicTracks.Add(NULL);
	MusicTracks.Add(MenuMusicObj.Object);
	MusicTracks.Add(ExplorationMusicObj.Object);
	MusicTracks.Add(TravelMusicObj.Object);
	MusicTracks.Add(PacificMusicObj.Object);
	MusicTracks.Add(DangerMusicObk.Object);
	MusicTracks.Add(CombatMusicObj.Object);
	MusicTracks.Add(WarMusicObj.Object);

	// Sound references
	TargetWarningSound = TargetWarningSoundObj.Object;
	AttackWarningSound = AttackWarningSoundObj.Object;
	HealthWarningSound = HealthWarningSoundObj.Object;
	HealthWarningHeavySound = HealthWarningHeavySoundObj.Object;

	// Music sound
	MusicPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("MusicSound"));
	MusicPlayer.Sound->bAutoActivate = false;
	MusicPlayer.Sound->bAutoDestroy = false;
	MusicPlayer.PitchedFade = false;
	MusicPlayer.FadeSpeed = 0.2;
	MusicPlayer.Volume = 0;

	// Power sound
	PowerPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("PowerSound"));
	PowerPlayer.Sound->bAutoActivate = false;
	PowerPlayer.Sound->bAutoDestroy = false;
	PowerPlayer.PitchedFade = true;
	PowerPlayer.FadeSpeed = 0.3;
	PowerPlayer.Volume = 0;

	// Engine sound
	EnginePlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("EngineSound"));
	EnginePlayer.Sound->bAutoActivate = false;
	EnginePlayer.Sound->bAutoDestroy = false;
	EnginePlayer.PitchedFade = true;
	EnginePlayer.FadeSpeed = 1.0;
	EnginePlayer.Volume = 0;

	// RCS sound
	RCSPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("RCSSound"));
	RCSPlayer.Sound->bAutoActivate = false;
	RCSPlayer.Sound->bAutoDestroy = false;
	RCSPlayer.PitchedFade = true;
	RCSPlayer.FadeSpeed = 2.0;
	RCSPlayer.Volume = 0;

	// Warning sound on targeting
	TargetWarningPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("TargetWarningSound"));
	TargetWarningPlayer.Sound->bAutoActivate = false;
	TargetWarningPlayer.Sound->bAutoDestroy = false;
	TargetWarningPlayer.PitchedFade = false;
	TargetWarningPlayer.FadeSpeed = 10.0f;
	TargetWarningPlayer.Volume = 0;

	// Warning sound on attack
	AttackWarningPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("AttackWarningSound"));
	AttackWarningPlayer.Sound->bAutoActivate = false;
	AttackWarningPlayer.Sound->bAutoDestroy = false;
	AttackWarningPlayer.PitchedFade = false;
	AttackWarningPlayer.FadeSpeed = 10.0f;
	AttackWarningPlayer.Volume = 0;

	// Warning sound on low health
	HealthWarningPlayer.Sound = PCIP.CreateDefaultSubobject<UAudioComponent>(this, TEXT("HealthWarningSound"));
	HealthWarningPlayer.Sound->bAutoActivate = false;
	HealthWarningPlayer.Sound->bAutoDestroy = false;
	HealthWarningPlayer.PitchedFade = false;
	HealthWarningPlayer.FadeSpeed = 1.0f;
	HealthWarningPlayer.Volume = 0;
}

/*----------------------------------------------------
	Gameplay
----------------------------------------------------*/

void UFlareSoundManager::Setup(AFlarePlayerController* Player)
{
	FLOG("UFlareSoundManager::Setup");
	PC = Player;

	// Attach all sounds
	if (Player)
	{
		USceneComponent* RootComponent = Player->GetRootComponent();
		FAttachmentTransformRules AttachRules(EAttachmentRule::KeepWorld, true);

		// Music
		MusicPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		MusicPlayer.Sound->SetSound(NULL);

		// Engine sounds
		PowerPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		EnginePlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		RCSPlayer.Sound->AttachToComponent(RootComponent, AttachRules);

		// Warning sounds
		TargetWarningPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		AttackWarningPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		HealthWarningPlayer.Sound->AttachToComponent(RootComponent, AttachRules);
		TargetWarningPlayer.Sound->SetSound(TargetWarningSound);
		AttackWarningPlayer.Sound->SetSound(AttackWarningSound);
		HealthWarningPlayer.Sound->SetSound(HealthWarningSound);

		// Device
		FAudioDevice* AudioDevice = Player->GetWorld()->GetAudioDevice();
		FCHECK(AudioDevice);
		FCHECK(MasterSoundClass);
		FCHECK(MasterSoundMix);
		AudioDevice->SetBaseSoundMix(MasterSoundMix);
	}
}

void UFlareSoundManager::SetMusicVolume(int32 Volume)
{
	FLOGV("UFlareSoundManager::SetMusicVolume %d", Volume);

	MusicVolume = FMath::Clamp(Volume / 10.0f, 0.0f, 1.0f);

	// Update
	FAudioDevice* AudioDevice = PC->GetWorld()->GetAudioDevice();
	FCHECK(AudioDevice);
	AudioDevice->SetSoundMixClassOverride(MasterSoundMix, MusicSoundClass, MusicVolume, 1.0f, 0.5f, true);
}

void UFlareSoundManager::SetMasterVolume(int32 Volume)
{
	FLOGV("UFlareSoundManager::SetMasterVolume %d", Volume);

	MasterVolume = FMath::Clamp(Volume / 10.0f, 0.0f, 1.0f);

	// Update
	FAudioDevice* AudioDevice = PC->GetWorld()->GetAudioDevice();
	FCHECK(AudioDevice);
	AudioDevice->SetSoundMixClassOverride(MasterSoundMix, MasterSoundClass, MasterVolume, 1.0f, 0.5f, true);
}

void UFlareSoundManager::Update(float DeltaSeconds)
{
	// Update the ship if necessary
	AFlareSpacecraft* PlayerShip = PC->GetShipPawn();
	if (PlayerShip != ShipPawn)
	{
		SetCurrentSpacecraft(PlayerShip);
	}

	// Music management
	if (MusicChanging)
	{
		if (MusicPlayer.Volume > 0)
		{
			UpdatePlayer(MusicPlayer, -DeltaSeconds);
		}
		else
		{
			SetDesiredMusicTrack();
		}
	}
	else if (MusicDesiredTrack == EFlareMusicTrack::None)
	{
		UpdatePlayer(MusicPlayer, -DeltaSeconds);
	}
	else
	{
		UpdatePlayer(MusicPlayer, +DeltaSeconds);
	}

	// Has a ship : update ship sounds
	if (ShipPawn)
	{
		// Engine values
		float RCSAlpha = 0;
		float EngineAlpha = 0;
		int32 RCSCount = 0;
		int32 EngineCount = 0;

		// Check all engines for engine alpha values
		TArray<UActorComponent*> Engines = ShipPawn->GetComponentsByClass(UFlareEngine::StaticClass());
		for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++)
		{
			UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]);
			if (Engine->IsA(UFlareOrbitalEngine::StaticClass()))
			{
				EngineAlpha += Engine->GetEffectiveAlpha();
				EngineCount++;
			}
			else
			{
				RCSAlpha += Engine->GetEffectiveAlpha();
				RCSCount++;
			}
		}

		// Get player threats
		bool Targeted, FiredUpon;
		UFlareSimulatedSpacecraft* Threat;
		PC->GetPlayerShipThreatStatus(Targeted, FiredUpon, Threat);
		UFlareSimulatedSpacecraftDamageSystem* DamageSystem = PlayerShip->GetParent()->GetDamageSystem();
		bool PlayerShipEndangered = (DamageSystem->IsCrewEndangered() || DamageSystem->IsUncontrollable()) && DamageSystem->IsAlive();

		// Update engine sounds
		UpdatePlayer(PowerPlayer,  (!DamageSystem->IsUncontrollable() && !DamageSystem->HasPowerOutage() ? 1 : -1) * DeltaSeconds);
		UpdatePlayer(EnginePlayer, (EngineAlpha > 0 ? EngineAlpha / EngineCount : -1) * DeltaSeconds);
		UpdatePlayer(RCSPlayer,    (RCSAlpha > 0 ? RCSAlpha / RCSCount : -1)          * DeltaSeconds);

		// Update alarms
		bool IsHeavy = (ShipPawn->GetParent()->GetDescription()->Size == EFlarePartSize::L);
		UpdatePlayer(TargetWarningPlayer, (!IsHeavy && !FiredUpon & Targeted ?  1.0f : -1.0f) * DeltaSeconds);
		UpdatePlayer(AttackWarningPlayer, (!IsHeavy &&  FiredUpon ?             1.0f : -1.0f) * DeltaSeconds);
		UpdatePlayer(HealthWarningPlayer, (PlayerShipEndangered ?               1.0f : -1.0f) * DeltaSeconds);
	}

	// No ship : stop all ship sounds
	else
	{
		UpdatePlayer(PowerPlayer,         -DeltaSeconds);
		UpdatePlayer(EnginePlayer,        -DeltaSeconds);
		UpdatePlayer(RCSPlayer,           -DeltaSeconds);
		UpdatePlayer(TargetWarningPlayer, -DeltaSeconds);
		UpdatePlayer(AttackWarningPlayer, -DeltaSeconds);
		UpdatePlayer(HealthWarningPlayer, -DeltaSeconds);
	}

	UpdateEffectsVolume(DeltaSeconds);
}

void UFlareSoundManager::RequestMusicTrack(EFlareMusicTrack::Type NewTrack)
{
	FLOGV("UFlareSoundManager::RequestMusicTrack : requested %d", (int32)(NewTrack - EFlareMusicTrack::None));
	if (NewTrack != MusicDesiredTrack)
	{
		FLOG("UFlareSoundManager::RequestMusicTrack : switching");
		MusicDesiredTrack = NewTrack;
		MusicChanging = true;
	}
}

/*----------------------------------------------------
	Internal methods
----------------------------------------------------*/

void UFlareSoundManager::SetCurrentSpacecraft(AFlareSpacecraft* Ship)
{
	ShipPawn = Ship;

	if (Ship)
	{
		// Setup power sound
		FFlareSpacecraftDescription* ShipDescription = Ship->GetParent()->GetDescription();
		PowerPlayer.Sound->SetSound(ShipDescription ? ShipDescription->PowerSound : NULL);

		// Setup orbital engine sound
		FFlareSpacecraftComponentDescription* EngineDescription = Ship->GetOrbitalEngineDescription();
		EnginePlayer.Sound->SetSound(EngineDescription ? EngineDescription->EngineCharacteristics.EngineSound : NULL);

		// Setup RCS sound
		FFlareSpacecraftComponentDescription* RCSDescription = Ship->GetRCSDescription();
		RCSPlayer.Sound->SetSound(RCSDescription ? RCSDescription->EngineCharacteristics.EngineSound : NULL);

		// Setup warning sound
		HealthWarningPlayer.Sound->Sound = ShipDescription->Size == EFlarePartSize::L ? HealthWarningHeavySound : HealthWarningSound;
	}
}

void UFlareSoundManager::SetDesiredMusicTrack()
{
	int32 TrackIndex = (int32)(MusicDesiredTrack - EFlareMusicTrack::None);
	FLOGV("UFlareSoundManager::SetDesiredMusicTrack : starting %d", TrackIndex);
	FCHECK(TrackIndex >= 0 && TrackIndex < MusicTracks.Num());

	MusicPlayer.Sound->SetSound(MusicTracks[TrackIndex]);
	MusicChanging = false;
}

void UFlareSoundManager::UpdateEffectsVolume(float DeltaSeconds)
{
	// Fade in menu transitions
	if (PC)
	{
		if (PC->GetMenuManager()->IsFading())
		{
			EffectsVolume -= DeltaSeconds / PC->GetMenuManager()->GetFadeDuration();
		}
		else
		{
			EffectsVolume += DeltaSeconds / PC->GetMenuManager()->GetFadeDuration();
		}
		EffectsVolume = FMath::Clamp(EffectsVolume, 0.01f, 1.0f);
	}

	// Get device and update
	FAudioDevice* AudioDevice = PC->GetWorld()->GetAudioDevice();
	FCHECK(AudioDevice);
	AudioDevice->SetSoundMixClassOverride(MasterSoundMix, EffectsSoundClass, EffectsVolume, 1.0f, 0.0f, true);
}

void UFlareSoundManager::UpdatePlayer(FFlareSoundPlayer& Player, float VolumeDelta)
{
	float NewVolume = FMath::Clamp(Player.Volume + VolumeDelta * Player.FadeSpeed, 0.0f, 1.0f);

	if (NewVolume != Player.Volume)
	{
		if (NewVolume == 0)
		{
			Player.Sound->Stop();
		}
		else if (Player.Volume == 0)
		{
			Player.Sound->Play();
		}
		else
		{
			Player.Sound->SetVolumeMultiplier(NewVolume);
			Player.Sound->SetPitchMultiplier(Player.PitchedFade ? 0.5f + 0.5f * NewVolume : 1.0f);
		}
		Player.Volume = NewVolume;
	}
}

void UFlareSoundManager::StopPlayer(FFlareSoundPlayer& Player)
{
	Player.Sound->Stop();
	Player.Volume = 0;
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>


<commit_before>// File_ExtendedModule - Info for Extended Module files
// Copyright (C) 2008-2010 MediaArea.net SARL, Info@MediaArea.net
//
// This library is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WXMHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILXMY or FXMNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library. If not, see <http://www.gnu.org/licenses/>.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
// Compilation conditions
#include "MediaInfo/Setup.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#if defined(MEDIAINFO_XM_YES)
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Audio/File_ExtendedModule.h"
//---------------------------------------------------------------------------

namespace MediaInfoLib
{

//***************************************************************************
// Static stuff
//***************************************************************************

//---------------------------------------------------------------------------
bool File_ExtendedModule::FileHeader_Begin()
{
    //Element_Size
    if (Buffer_Size<38)
        return false; //Must wait for more data

    if (CC8(Buffer)!=0x457874656E646564LL || CC8(Buffer+8)!=0x204D6F64756C653ALL  //"Extended Module: "
     || CC1(Buffer+16)!=0x20 || CC1(Buffer+37)!=0x1A)
    {
        Reject("Extended Module");
        return false;
    }

    //All should be OK...
    return true;
}

//***************************************************************************
// Buffer - Global
//***************************************************************************

//---------------------------------------------------------------------------
void File_ExtendedModule::Read_Buffer_Continue()
{
    //Parsing
    Ztring ModuleName, TrackerName;
    int32u HeaderSize;
    int16u Length, Channels, Patterns, Instruments, Flags, Tempo, BPM;
    int8u  VersionMinor, VersionMajor;
    Skip_String(17,                                             "Signature");
    Get_Local(20, ModuleName,                                   "Module name");
    Skip_L1(                                                    "0x1A");
    Get_Local(20, TrackerName,                                  "Tracker name");
    Get_L1 (VersionMinor,                                       "Version (minor)");
    Get_L1 (VersionMajor,                                       "Version (major)");
    Get_L4 (HeaderSize,                                         "Header size");
    Get_L2 (Length,                                             "Song Length");
    Skip_L2(                                                    "Restart position");
    Get_L2 (Channels,                                           "Number of channels");
    Get_L2 (Patterns,                                           "Number of patterns");
    Get_L2 (Instruments,                                        "Number of instruments");
    Get_L2 (Flags,                                              "Flags");
    Get_L2 (Tempo,                                              "Tempo");
    Get_L2 (BPM,                                                "BPM");
    Skip_XX(256,                                                "Pattern order table");

    FILLING_BEGIN();
        Accept("Extended Module");

        Fill(Stream_General, 0, General_Format, "Extended Module");
        Fill(Stream_General, 0, General_Format_Version, Ztring(_T("Version"))+Ztring::ToZtring(VersionMajor)+_T(".")+Ztring::ToZtring(VersionMinor/10)+Ztring::ToZtring(VersionMinor%10));
        Fill(Stream_General, 0, General_Track, ModuleName.Trim(_T(' ')));
        Fill(Stream_General, 0, General_Encoded_Application, TrackerName.Trim(_T(' ')));
        Fill(Stream_General, 0, "Tempo", Tempo);
        Fill(Stream_General, 0, "BPM", BPM);

        for (int16u Pos=0; Pos<Channels; Pos++)
        {
            Stream_Prepare(Stream_Audio);
        }

        //No more need data
        Finish("Extended Module");
    FILLING_END();
}

//***************************************************************************
// C++
//***************************************************************************

} //NameSpace

#endif //MEDIAINFO_XM_YES
<commit_msg>x ExtendedModule: some tunning of output<commit_after>// File_ExtendedModule - Info for Extended Module files
// Copyright (C) 2008-2010 MediaArea.net SARL, Info@MediaArea.net
//
// This library is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WXMHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILXMY or FXMNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library. If not, see <http://www.gnu.org/licenses/>.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
// Compilation conditions
#include "MediaInfo/Setup.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#if defined(MEDIAINFO_XM_YES)
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Audio/File_ExtendedModule.h"
//---------------------------------------------------------------------------

namespace MediaInfoLib
{

//***************************************************************************
// Static stuff
//***************************************************************************

//---------------------------------------------------------------------------
bool File_ExtendedModule::FileHeader_Begin()
{
    //Element_Size
    if (Buffer_Size<38)
        return false; //Must wait for more data

    if (CC8(Buffer)!=0x457874656E646564LL || CC8(Buffer+8)!=0x204D6F64756C653ALL  //"Extended Module: "
     || CC1(Buffer+16)!=0x20 || CC1(Buffer+37)!=0x1A)
    {
        Reject("Extended Module");
        return false;
    }

    //All should be OK...
    return true;
}

//***************************************************************************
// Buffer - Global
//***************************************************************************

//---------------------------------------------------------------------------
void File_ExtendedModule::Read_Buffer_Continue()
{
    //Parsing
    Ztring ModuleName, TrackerName;
    int32u HeaderSize;
    int16u Length, Channels, Patterns, Instruments, Flags, Tempo, BPM;
    int8u  VersionMinor, VersionMajor;
    Skip_String(17,                                             "Signature");
    Get_Local(20, ModuleName,                                   "Module name");
    Skip_L1(                                                    "0x1A");
    Get_Local(20, TrackerName,                                  "Tracker name");
    Get_L1 (VersionMinor,                                       "Version (minor)");
    Get_L1 (VersionMajor,                                       "Version (major)");
    Get_L4 (HeaderSize,                                         "Header size");
    Get_L2 (Length,                                             "Song Length");
    Skip_L2(                                                    "Restart position");
    Get_L2 (Channels,                                           "Number of channels");
    Get_L2 (Patterns,                                           "Number of patterns");
    Get_L2 (Instruments,                                        "Number of instruments");
    Get_L2 (Flags,                                              "Flags");
    Get_L2 (Tempo,                                              "Tempo");
    Get_L2 (BPM,                                                "BPM");
    Skip_XX(256,                                                "Pattern order table");

    FILLING_BEGIN();
        Accept("Extended Module");

        Fill(Stream_General, 0, General_Format, "Extended Module");
        Fill(Stream_General, 0, General_Format_Version, Ztring::ToZtring(VersionMajor)+_T(".")+Ztring::ToZtring(VersionMinor/10)+Ztring::ToZtring(VersionMinor%10));
        Fill(Stream_General, 0, General_Track, ModuleName.Trim(_T(' ')));
        Fill(Stream_General, 0, General_Encoded_Application, TrackerName.Trim(_T(' ')));
        Fill(Stream_General, 0, "Tempo", Tempo);
        Fill(Stream_General, 0, "BPM", BPM);

        Stream_Prepare(Stream_Audio);
        Fill(Stream_Audio, 0, "Sampler, Channels", Channels);
        Fill(Stream_Audio, 0, "Sampler, Patterns", Patterns);
        Fill(Stream_Audio, 0, "Sampler, Instruments", Instruments);

        //No more need data
        Finish("Extended Module");
    FILLING_END();
}

//***************************************************************************
// C++
//***************************************************************************

} //NameSpace

#endif //MEDIAINFO_XM_YES
<|endoftext|>


<commit_before>//
//  Created by Derek van Vliet on 2014-12-10.
//  Copyright (c) 2014 Get Set Games Inc. All rights reserved.
//

#include "SharingPrivatePCH.h"

void UScreenshotComponent::OnRegister()
{
	Super::OnRegister();

	GEngine->GameViewport->OnScreenshotCaptured().AddUObject(this, &UScreenshotComponent::ScreenshotCapturedDelegate_Handler);
}

void UScreenshotComponent::OnUnregister()
{
	Super::OnUnregister();

	GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this);
}

void UScreenshotComponent::ScreenshotCapturedDelegate_Handler(int32 Width, int32 Height, const TArray<FColor>& Pixels)
{
	if (ScreenshotCapturedTexture.IsBound())
	{
		UTexture2D* Texture = UTexture2D::CreateTransient(Width, Height);
		FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0];

		uint8* MipData = static_cast<uint8*>(Mip.BulkData.Lock(LOCK_READ_WRITE));

		for(int32 y = 0; y < Height; y++)
		{
			uint8* DestPtr = &MipData[(Height - 1 - y) * Width * sizeof(FColor)];
			int32 PixelIndex = (Height - 1 - y) * Width;
			if (Pixels.IsValidIndex(PixelIndex))
			{
				FColor* SrcPtr = const_cast<FColor*>(&Pixels[PixelIndex]);
				for(int32 x = 0; x < Width; x++)
				{
					*DestPtr++ = SrcPtr->B;
					*DestPtr++ = SrcPtr->G;
					*DestPtr++ = SrcPtr->R;
					*DestPtr++ = 0xFF;
					SrcPtr++;
				}
			}
		}

		Mip.BulkData.Unlock();

		Texture->SRGB = true;
		Texture->CompressionSettings = TC_Default;
		Texture->UpdateResource();

		ScreenshotCapturedTexture.Broadcast(Texture);
	}

	if (ScreenshotCapturedImage.IsBound())
	{
		ScreenshotCapturedImage.Broadcast(FScreenshotImage(Pixels,Width,Height));
	}
}
<commit_msg>vertically flipped screenshots on iOS<commit_after>//
//  Created by Derek van Vliet on 2014-12-10.
//  Copyright (c) 2014 Get Set Games Inc. All rights reserved.
//

#include "SharingPrivatePCH.h"

void UScreenshotComponent::OnRegister()
{
	Super::OnRegister();

	GEngine->GameViewport->OnScreenshotCaptured().AddUObject(this, &UScreenshotComponent::ScreenshotCapturedDelegate_Handler);
}

void UScreenshotComponent::OnUnregister()
{
	Super::OnUnregister();

	GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this);
}

void UScreenshotComponent::ScreenshotCapturedDelegate_Handler(int32 Width, int32 Height, const TArray<FColor>& Pixels)
{
	if (ScreenshotCapturedTexture.IsBound())
	{
		UTexture2D* Texture = UTexture2D::CreateTransient(Width, Height);
		FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0];

		uint8* MipData = static_cast<uint8*>(Mip.BulkData.Lock(LOCK_READ_WRITE));

		for(int32 y = Height-1; y >= 0; y--)
		{
			uint8* DestPtr = &MipData[y * Width * sizeof(FColor)];
			int32 PixelIndex = y * Width;
#if PLATFORM_IOS
			PixelIndex = Pixels.Num() - PixelIndex - 1;
#endif
			if (Pixels.IsValidIndex(PixelIndex))
			{
				FColor* SrcPtr = const_cast<FColor*>(&Pixels[PixelIndex]);
				for(int32 x = 0; x < Width; x++)
				{
					*DestPtr++ = SrcPtr->B;
					*DestPtr++ = SrcPtr->G;
					*DestPtr++ = SrcPtr->R;
					*DestPtr++ = 0xFF;
					SrcPtr++;
				}
			}
		}

		Mip.BulkData.Unlock();

		Texture->SRGB = true;
		Texture->CompressionSettings = TC_Default;
		Texture->UpdateResource();

		ScreenshotCapturedTexture.Broadcast(Texture);
	}

	if (ScreenshotCapturedImage.IsBound())
	{
		ScreenshotCapturedImage.Broadcast(FScreenshotImage(Pixels,Width,Height));
	}
}
<|endoftext|>


<commit_before>#include "config.h"

#include <immintrin.h>
#include <x86intrin.h>

#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cassert>

#include <memory>
#include <set>
#include <string>
#include <chrono>

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

#include "popcnt-lookup.cpp"
#include "popcnt-bit-parallel-scalar.cpp"
#include "popcnt-harley-seal.cpp"

#include "sse_operators.cpp"
#include "popcnt-sse-bit-parallel.cpp"
#include "popcnt-sse-bit-parallel-better.cpp"
#include "popcnt-sse-harley-seal.cpp"
#include "popcnt-sse-lookup.cpp"
#include "popcnt-sse-cpu.cpp"

#include "popcnt-cpu.cpp"
#include "popcnt-builtin.cpp"

#if defined(HAVE_AVX2_INSTRUCTIONS)
#   include "popcnt-avx2-lookup.cpp"
#   include "popcnt-avx2-harley-seal.cpp"
#   include "popcnt-avx2-cpu.cpp"
#endif

#include "function_registry.cpp"

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

class Error final {
    std::string message;

public:
    Error(const std::string& msg) : message(msg) {}

public:
    const char* c_str() const {
        return message.c_str();
    }
};

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

class CommandLine final {

public:
    bool    print_help;
    bool    print_csv;
    size_t  size;
    size_t  iteration_count;
    std::string executable;
    std::set<std::string> functions;

public:
    CommandLine(int argc, char* argv[], const FunctionRegistry& names);
};

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

CommandLine::CommandLine(int argc, char* argv[], const FunctionRegistry& names)
    : print_help(false)
    , print_csv(false)
    , size(0)
    , iteration_count(0) {

    int positional = 0;
    for (int i=1; i < argc; i++) {
        const std::string arg = argv[i];

        if (arg == "--help" || arg == "-h") {
            print_help = true;
            return;
        }

        if (arg == "--csv") {
            print_csv = true;
            continue;
        }

        // positional arguments
        if (positional == 0) {
            int tmp = std::atoi(arg.c_str());
            if (tmp <= 0) {
                throw Error("Size must be greater than 0.");
            }

            if (tmp % 32 != 0) {
                throw Error("Size must be divisible by 32.");
            }

            size = tmp;

        } else if (positional == 1) {

            int tmp = std::atoi(arg.c_str());
            if (tmp <= 0) {
                throw Error("Iteration count must be greater than 0.");
            }

            iteration_count = tmp;
        } else {
            if (names.has(arg)) {
                functions.insert(std::move(arg));
            } else {
                throw Error("'" + arg + "' is not valid function name");
            }
        }

        positional += 1;
    }

    if (positional < 2) {
        print_help = true;
    }
}

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

class Application final {

    const CommandLine& cmd;
    const FunctionRegistry& names;
    uint8_t* data;

    uint64_t count;
    double   time;

    struct Result {
        uint64_t count;
        double   time;
    };

public:
    Application(const CommandLine& cmdline, const FunctionRegistry& names);
    ~Application();

    int run();

private:
    void print_help();
    void run_procedures();
    void run_procedure(const std::string& name);

    template <typename FN>
    Result run(const std::string& name, FN function, double reference);
};

Application::Application(const CommandLine& cmdline, const FunctionRegistry& names)
    : cmd(cmdline)
    , names(names)
    , data(nullptr) {}

int Application::run() {

    if (cmd.print_help) {
        print_help();
    } else {
        run_procedures();
    }

    return 0;
}

void Application::run_procedures() {

    // GCC parses alignof(), but does not implement it...
    int result = posix_memalign(reinterpret_cast<void**>(&data), 64, cmd.size);
    if (result) {
        throw Error(std::string("posix_memalign failed: ") + strerror(result));
    }

    for (size_t i=0; i < cmd.size; i++) {
        data[i] = i;
    }

    count = 0;
    time  = 0;

    if (!cmd.functions.empty()) {
        for (const auto& name: cmd.functions) {
            run_procedure(name);
        }
    } else {
        for (const auto& name: names.get_available()) {
            run_procedure(name);
        }
    }
}

Application::~Application() {
    free(data);
}

void Application::run_procedure(const std::string& name) {

#define RUN(function_name, function) \
    if (name == function_name) { \
        auto result = run(name, function, time); \
        count += result.count; \
        if (time == 0.0) { \
            time = result.time; \
        } \
    }

    RUN("lookup-8",                     popcnt_lookup_8bit)
    RUN("lookup-64",                    popcnt_lookup_64bit);
    RUN("bit-parallel",                 popcnt_parallel_64bit_naive);
    RUN("bit-parallel-optimized",       popcnt_parallel_64bit_optimized);
    RUN("bit-parallel-mul",             popcnt_parallel_64bit_mul);
    RUN("harley-seal",                  popcnt_harley_seal);
    RUN("sse-bit-parallel",             popcnt_SSE_bit_parallel);
    RUN("sse-bit-parallel-original",    popcnt_SSE_bit_parallel_original);
    RUN("sse-bit-parallel-better",      popcnt_SSE_bit_parallel_better);
    RUN("sse-harley-seal",              popcnt_SSE_harley_seal);
    RUN("sse-lookup",                   popcnt_SSE_lookup);
    RUN("sse-lookup-original",          popcnt_SSE_lookup_original);
    RUN("sse-cpu",                      popcnt_SSE_and_cpu);

#if defined(HAVE_AVX2_INSTRUCTIONS)
    RUN("avx2-lookup",          popcnt_AVX2_lookup);
    RUN("avx2-lookup-original", popcnt_AVX2_lookup_original);
    RUN("avx2-harley-seal",     popcnt_AVX2_harley_seal);
#endif

#if defined(HAVE_POPCNT_INSTRUCTION)
    RUN("cpu", popcnt_cpu_64bit);
#endif

#define RUN_BUILTIN(function_name, function) \
    { \
        auto wrapper = [](const uint8_t* data, size_t size) { \
            return function(reinterpret_cast<const uint64_t*>(data), size/8); \
        }; \
        RUN(function_name, wrapper); \
    }

    RUN_BUILTIN("builtin-popcnt",                           builtin_popcnt);
    RUN_BUILTIN("builtin-popcnt32",                         builtin_popcnt32);
    RUN_BUILTIN("builtin-popcnt-unrolled",                  builtin_popcnt_unrolled);
    RUN_BUILTIN("builtin-popcnt-unrolled32",                builtin_popcnt_unrolled32);
    RUN_BUILTIN("builtin-popcnt-unrolled-errata",           builtin_popcnt_unrolled_errata);
    RUN_BUILTIN("builtin-popcnt-unrolled-errata-manual",    builtin_popcnt_unrolled_errata_manual);
    RUN_BUILTIN("builtin-popcnt-movdq",                     builtin_popcnt_movdq);
    RUN_BUILTIN("builtin-popcnt-movdq-unrolled",            builtin_popcnt_movdq_unrolled);
    RUN_BUILTIN("builtin-popcnt-movdq-unrolled_manual",     builtin_popcnt_movdq_unrolled_manual);
}

template <typename FN>
Application::Result Application::run(const std::string& name, FN function, double reference) {

    Result result;

    if (cmd.print_csv) {
        printf("%s, %lu, %lu, ", name.c_str(), cmd.size, cmd.iteration_count);
        fflush(stdout);
    } else {
        const auto& dsc = names.get(name);
        printf("%*s ... ", -names.get_widest_name(), dsc.name.c_str());
        fflush(stdout);
    }

    size_t n = 0;
    size_t k = cmd.iteration_count;

    const auto t1 = std::chrono::high_resolution_clock::now();
    while (k-- > 0) {
        n += function(data, cmd.size);
    }

    const auto t2 = std::chrono::high_resolution_clock::now();

    const std::chrono::duration<double> td = t2-t1;

    if (cmd.print_csv) {
        printf("%0.6f\n", td.count());
    } else {
        //printf("reference result = %lu, time = %0.6f s", n, td.count());
        printf("time = %0.6f s", td.count());

        if (reference > 0.0) {
            const auto speedup = reference/td.count();

            printf(" (speedup: %3.2f)", speedup);
        }

        putchar('\n');
    }

    result.count = n; // to prevent compiler from optimizing out the loop
    result.time  = td.count();

    return result;
}

void Application::print_help() {
    std::printf("usage: %s [--csv] buffer_size iteration_count [function(s)]\n", cmd.executable.c_str());
    std::puts("");
    std::puts("--csv               - print results in CVS format:");
    std::puts("                      function name, buffer_size, iteration_count, time");
    std::puts("");
    std::puts("1. buffer_size      - size of buffer in bytes");
    std::puts("2. iteration_count  - as the name states");

    std::puts("3. one or more functions (if not given all will run):");

    const int w = names.get_widest_name();
    for (const auto& item: names.get_functions()) {

        std::printf("  * %*s - %s\n", -w, item.name.c_str(), item.help.c_str());
    }
}

int main(int argc, char* argv[]) {

    try {
        FunctionRegistry names;
        CommandLine cmd(argc, argv, names);
        Application app(cmd, names);

        return app.run();
    } catch (Error& e) {
        puts(e.c_str());

        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

<commit_msg>plug missing procedure into speed utility<commit_after>#include "config.h"

#include <immintrin.h>
#include <x86intrin.h>

#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cassert>

#include <memory>
#include <set>
#include <string>
#include <chrono>

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

#include "popcnt-lookup.cpp"
#include "popcnt-bit-parallel-scalar.cpp"
#include "popcnt-harley-seal.cpp"

#include "sse_operators.cpp"
#include "popcnt-sse-bit-parallel.cpp"
#include "popcnt-sse-bit-parallel-better.cpp"
#include "popcnt-sse-harley-seal.cpp"
#include "popcnt-sse-lookup.cpp"
#include "popcnt-sse-cpu.cpp"

#include "popcnt-cpu.cpp"
#include "popcnt-builtin.cpp"

#if defined(HAVE_AVX2_INSTRUCTIONS)
#   include "popcnt-avx2-lookup.cpp"
#   include "popcnt-avx2-harley-seal.cpp"
#   include "popcnt-avx2-cpu.cpp"
#endif

#include "function_registry.cpp"

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

class Error final {
    std::string message;

public:
    Error(const std::string& msg) : message(msg) {}

public:
    const char* c_str() const {
        return message.c_str();
    }
};

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

class CommandLine final {

public:
    bool    print_help;
    bool    print_csv;
    size_t  size;
    size_t  iteration_count;
    std::string executable;
    std::set<std::string> functions;

public:
    CommandLine(int argc, char* argv[], const FunctionRegistry& names);
};

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

CommandLine::CommandLine(int argc, char* argv[], const FunctionRegistry& names)
    : print_help(false)
    , print_csv(false)
    , size(0)
    , iteration_count(0) {

    int positional = 0;
    for (int i=1; i < argc; i++) {
        const std::string arg = argv[i];

        if (arg == "--help" || arg == "-h") {
            print_help = true;
            return;
        }

        if (arg == "--csv") {
            print_csv = true;
            continue;
        }

        // positional arguments
        if (positional == 0) {
            int tmp = std::atoi(arg.c_str());
            if (tmp <= 0) {
                throw Error("Size must be greater than 0.");
            }

            if (tmp % 32 != 0) {
                throw Error("Size must be divisible by 32.");
            }

            size = tmp;

        } else if (positional == 1) {

            int tmp = std::atoi(arg.c_str());
            if (tmp <= 0) {
                throw Error("Iteration count must be greater than 0.");
            }

            iteration_count = tmp;
        } else {
            if (names.has(arg)) {
                functions.insert(std::move(arg));
            } else {
                throw Error("'" + arg + "' is not valid function name");
            }
        }

        positional += 1;
    }

    if (positional < 2) {
        print_help = true;
    }
}

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

class Application final {

    const CommandLine& cmd;
    const FunctionRegistry& names;
    uint8_t* data;

    uint64_t count;
    double   time;

    struct Result {
        uint64_t count;
        double   time;
    };

public:
    Application(const CommandLine& cmdline, const FunctionRegistry& names);
    ~Application();

    int run();

private:
    void print_help();
    void run_procedures();
    void run_procedure(const std::string& name);

    template <typename FN>
    Result run(const std::string& name, FN function, double reference);
};

Application::Application(const CommandLine& cmdline, const FunctionRegistry& names)
    : cmd(cmdline)
    , names(names)
    , data(nullptr) {}

int Application::run() {

    if (cmd.print_help) {
        print_help();
    } else {
        run_procedures();
    }

    return 0;
}

void Application::run_procedures() {

    // GCC parses alignof(), but does not implement it...
    int result = posix_memalign(reinterpret_cast<void**>(&data), 64, cmd.size);
    if (result) {
        throw Error(std::string("posix_memalign failed: ") + strerror(result));
    }

    for (size_t i=0; i < cmd.size; i++) {
        data[i] = i;
    }

    count = 0;
    time  = 0;

    if (!cmd.functions.empty()) {
        for (const auto& name: cmd.functions) {
            run_procedure(name);
        }
    } else {
        for (const auto& name: names.get_available()) {
            run_procedure(name);
        }
    }
}

Application::~Application() {
    free(data);
}

void Application::run_procedure(const std::string& name) {

#define RUN(function_name, function) \
    if (name == function_name) { \
        auto result = run(name, function, time); \
        count += result.count; \
        if (time == 0.0) { \
            time = result.time; \
        } \
    }

    RUN("lookup-8",                     popcnt_lookup_8bit)
    RUN("lookup-64",                    popcnt_lookup_64bit);
    RUN("bit-parallel",                 popcnt_parallel_64bit_naive);
    RUN("bit-parallel-optimized",       popcnt_parallel_64bit_optimized);
    RUN("bit-parallel-mul",             popcnt_parallel_64bit_mul);
    RUN("harley-seal",                  popcnt_harley_seal);
    RUN("sse-bit-parallel",             popcnt_SSE_bit_parallel);
    RUN("sse-bit-parallel-original",    popcnt_SSE_bit_parallel_original);
    RUN("sse-bit-parallel-better",      popcnt_SSE_bit_parallel_better);
    RUN("sse-harley-seal",              popcnt_SSE_harley_seal);
    RUN("sse-lookup",                   popcnt_SSE_lookup);
    RUN("sse-lookup-original",          popcnt_SSE_lookup_original);
    RUN("sse-cpu",                      popcnt_SSE_and_cpu);

#if defined(HAVE_AVX2_INSTRUCTIONS)
    RUN("avx2-lookup",          popcnt_AVX2_lookup);
    RUN("avx2-lookup-original", popcnt_AVX2_lookup_original);
    RUN("avx2-harley-seal",     popcnt_AVX2_harley_seal);
    RUN("avx2-cpu",             popcnt_AVX2_and_cpu);
#endif

#if defined(HAVE_POPCNT_INSTRUCTION)
    RUN("cpu", popcnt_cpu_64bit);
#endif

#define RUN_BUILTIN(function_name, function) \
    { \
        auto wrapper = [](const uint8_t* data, size_t size) { \
            return function(reinterpret_cast<const uint64_t*>(data), size/8); \
        }; \
        RUN(function_name, wrapper); \
    }

    RUN_BUILTIN("builtin-popcnt",                           builtin_popcnt);
    RUN_BUILTIN("builtin-popcnt32",                         builtin_popcnt32);
    RUN_BUILTIN("builtin-popcnt-unrolled",                  builtin_popcnt_unrolled);
    RUN_BUILTIN("builtin-popcnt-unrolled32",                builtin_popcnt_unrolled32);
    RUN_BUILTIN("builtin-popcnt-unrolled-errata",           builtin_popcnt_unrolled_errata);
    RUN_BUILTIN("builtin-popcnt-unrolled-errata-manual",    builtin_popcnt_unrolled_errata_manual);
    RUN_BUILTIN("builtin-popcnt-movdq",                     builtin_popcnt_movdq);
    RUN_BUILTIN("builtin-popcnt-movdq-unrolled",            builtin_popcnt_movdq_unrolled);
    RUN_BUILTIN("builtin-popcnt-movdq-unrolled_manual",     builtin_popcnt_movdq_unrolled_manual);
}

template <typename FN>
Application::Result Application::run(const std::string& name, FN function, double reference) {

    Result result;

    if (cmd.print_csv) {
        printf("%s, %lu, %lu, ", name.c_str(), cmd.size, cmd.iteration_count);
        fflush(stdout);
    } else {
        const auto& dsc = names.get(name);
        printf("%*s ... ", -names.get_widest_name(), dsc.name.c_str());
        fflush(stdout);
    }

    size_t n = 0;
    size_t k = cmd.iteration_count;

    const auto t1 = std::chrono::high_resolution_clock::now();
    while (k-- > 0) {
        n += function(data, cmd.size);
    }

    const auto t2 = std::chrono::high_resolution_clock::now();

    const std::chrono::duration<double> td = t2-t1;

    if (cmd.print_csv) {
        printf("%0.6f\n", td.count());
    } else {
        //printf("reference result = %lu, time = %0.6f s", n, td.count());
        printf("time = %0.6f s", td.count());

        if (reference > 0.0) {
            const auto speedup = reference/td.count();

            printf(" (speedup: %3.2f)", speedup);
        }

        putchar('\n');
    }

    result.count = n; // to prevent compiler from optimizing out the loop
    result.time  = td.count();

    return result;
}

void Application::print_help() {
    std::printf("usage: %s [--csv] buffer_size iteration_count [function(s)]\n", cmd.executable.c_str());
    std::puts("");
    std::puts("--csv               - print results in CVS format:");
    std::puts("                      function name, buffer_size, iteration_count, time");
    std::puts("");
    std::puts("1. buffer_size      - size of buffer in bytes");
    std::puts("2. iteration_count  - as the name states");

    std::puts("3. one or more functions (if not given all will run):");

    const int w = names.get_widest_name();
    for (const auto& item: names.get_functions()) {

        std::printf("  * %*s - %s\n", -w, item.name.c_str(), item.help.c_str());
    }
}

int main(int argc, char* argv[]) {

    try {
        FunctionRegistry names;
        CommandLine cmd(argc, argv, names);
        Application app(cmd, names);

        return app.run();
    } catch (Error& e) {
        puts(e.c_str());

        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <memory>

#include "input_data.cpp"
#include "gettime.cpp"
#include "quicksort-all.cpp"

class PerformanceTest final {

    int         iterations;
    InputData&  input;
    uint32_t*   tmp;

public:
    PerformanceTest(int n, InputData& input)
        : iterations(n)
        , input(input) {

        assert(iterations > 0);
        tmp = new uint32_t[input.count()];
    }

    ~PerformanceTest() {
        delete[] tmp;
    }

public:
    template <typename SORT_FUNCTION>
    uint32_t run(SORT_FUNCTION sort) {

        uint32_t time = 0;

        int k = iterations;
        while (k--) {
            memcpy(tmp, input.pointer(), input.size());

            const uint32_t t1 = get_time();
            sort(input.pointer(), 0, input.count() - 1);
            const uint32_t t2 = get_time();

            const uint32_t dt = t2 - t1;

            if (time == 0) {
                time = dt;
            } else if (dt < time) {
                time = dt;
            }
        }

        return time;
    }
};

enum class InputType {
    random,
    ascending,
    descending,
};

const char* as_string(InputType type) {
    switch (type) {
        case InputType::random:
            return "random";

        case InputType::ascending:
            return "ascending";

        case InputType::descending:
            return "descending";

        default:
            return "<unknown>";
    }
}

void std_sort_wrapper(uint32_t* array, int left, int right) {

    std::sort(array + left, array + right + 1);
}

class Test {

    std::unique_ptr<InputData> data;
    InputType   type;
    size_t      count;
    int         iterations;

public:
    Test(InputType type, size_t count, int iterations)
        : type(type)
        , count(count)
        , iterations(iterations) {

        switch (type) {
            case InputType::random:
                data.reset(new InputRandom(count));
                break;

            case InputType::ascending:
                data.reset(new InputAscending(count));
                break;

            case InputType::descending:
                data.reset(new InputDescending(count));
                break;
        }
    }

    void run() {

        printf("items count: %lu (%lu bytes), input %s\n", data->count(), data->size(), as_string(type));

        uint32_t ref = 0;
        ref = measure("std::sort",              std_sort_wrapper,             ref);
        measure("quick sort",                   quicksort,                    ref);
#ifdef HAVE_AVX2_INSTRUCTIONS
        measure("AVX2 quick sort",              qs::avx2::quicksort,          ref);
#endif
#ifdef HAVE_AVX512F_INSTRUCTIONS
        measure("AVX512 quick sort",            qs::avx512::quicksort,        ref);
        measure("AVX512 + popcnt quick sort",   qs::avx512::popcnt_quicksort, ref);
        measure("AVX512 + BMI2 quick sort",     qs::avx512::bmi2_quicksort,   ref);
#endif
    }

private:
    template <typename SORT_FUNCTION>
    uint32_t measure(const char* name, SORT_FUNCTION sort, uint32_t ref) {

        PerformanceTest test(iterations, *data);

        printf("%30s ... ", name); fflush(stdout);
        uint32_t time = test.run(sort);
        if (ref > 0) {
            printf("%0.4f s (%0.2f)\n", time/1000000.0, ref/double(time));
        } else {
            printf("%0.4f s\n", time/1000000.0);
        }

        return time;
    }
};

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

void usage() {
    puts("usage:");
    puts("speed SIZE ITERATIONS INPUT");
    puts("");
    puts("where");
    puts("* SIZE       - number of 32-bit elements");
    puts("* ITERATIONS - number of iterations");
    puts("* INPUT      - one of:");
    puts("                 ascending (or asc)");
    puts("                 descending (or dsc, desc)");
    puts("                 random (or rnd, rand)");
}

int main(int argc, char* argv[]) {

    if (argc < 4) {
        usage();
        return EXIT_FAILURE;
    }

    int count      = atoi(argv[1]);
    int iterations = atoi(argv[2]);
    InputType type;

#define is_keyword(key) (strcmp(argv[3], key) == 0)
    if (is_keyword("descending") || is_keyword("desc") || is_keyword("dsc")) {
        type = InputType::descending;
    } else if (is_keyword("ascending") || is_keyword("asc")) {
        type = InputType::ascending;
    } else if (is_keyword("random") || is_keyword("rand") || is_keyword("rnd")) {
        type = InputType::ascending;
    } else {
        usage();
        return EXIT_FAILURE;
    }
#undef is_keyword

    Test test(type, count, iterations);
    test.run();

    return EXIT_SUCCESS;
}
<commit_msg>AVX512 quicksort: fix speed utility<commit_after>#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <memory>

#include "input_data.cpp"
#include "gettime.cpp"
#include "quicksort-all.cpp"

class PerformanceTest final {

    int         iterations;
    InputData&  input;
    uint32_t*   tmp;

public:
    PerformanceTest(int n, InputData& input)
        : iterations(n)
        , input(input) {

        assert(iterations > 0);
        tmp = new uint32_t[input.count()];
    }

    ~PerformanceTest() {
        delete[] tmp;
    }

public:
    template <typename SORT_FUNCTION>
    uint32_t run(SORT_FUNCTION sort) {

        uint32_t time = 0;

        int k = iterations;
        while (k--) {
            memcpy(tmp, input.pointer(), input.size());

            const uint32_t t1 = get_time();
            sort(input.pointer(), 0, input.count() - 1);
            const uint32_t t2 = get_time();

            const uint32_t dt = t2 - t1;

            if (time == 0) {
                time = dt;
            } else if (dt < time) {
                time = dt;
            }
        }

        return time;
    }
};

enum class InputType {
    random,
    ascending,
    descending,
};

const char* as_string(InputType type) {
    switch (type) {
        case InputType::random:
            return "random";

        case InputType::ascending:
            return "ascending";

        case InputType::descending:
            return "descending";

        default:
            return "<unknown>";
    }
}

void std_sort_wrapper(uint32_t* array, int left, int right) {

    std::sort(array + left, array + right + 1);
}

class Test {

    std::unique_ptr<InputData> data;
    InputType   type;
    size_t      count;
    int         iterations;

public:
    Test(InputType type, size_t count, int iterations)
        : type(type)
        , count(count)
        , iterations(iterations) {

        switch (type) {
            case InputType::random:
                data.reset(new InputRandom(count));
                break;

            case InputType::ascending:
                data.reset(new InputAscending(count));
                break;

            case InputType::descending:
                data.reset(new InputDescending(count));
                break;
        }
    }

    void run() {

        printf("items count: %lu (%lu bytes), input %s\n", data->count(), data->size(), as_string(type));

        uint32_t ref = 0;
        ref = measure("std::sort",              std_sort_wrapper,             ref);
        measure("quick sort",                   quicksort,                    ref);
#ifdef HAVE_AVX2_INSTRUCTIONS
        measure("AVX2 quick sort",              qs::avx2::quicksort,          ref);
#endif
#ifdef HAVE_AVX512F_INSTRUCTIONS
        measure("AVX512 quick sort",            qs::avx512::quicksort,        ref);
        measure("AVX512 + popcnt quick sort",   qs::avx512::popcnt_quicksort, ref);
        measure("AVX512 + BMI2 quick sort",     qs::avx512::bmi2_quicksort,   ref);
#endif
    }

private:
    template <typename SORT_FUNCTION>
    uint32_t measure(const char* name, SORT_FUNCTION sort, uint32_t ref) {

        PerformanceTest test(iterations, *data);

        printf("%30s ... ", name); fflush(stdout);
        uint32_t time = test.run(sort);
        if (ref > 0) {
            printf("%0.4f s (%0.2f)\n", time/1000000.0, ref/double(time));
        } else {
            printf("%0.4f s\n", time/1000000.0);
        }

        return time;
    }
};

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

void usage() {
    puts("usage:");
    puts("speed SIZE ITERATIONS INPUT");
    puts("");
    puts("where");
    puts("* SIZE       - number of 32-bit elements");
    puts("* ITERATIONS - number of iterations");
    puts("* INPUT      - one of:");
    puts("                 ascending (or asc)");
    puts("                 descending (or dsc, desc)");
    puts("                 random (or rnd, rand)");
}

int main(int argc, char* argv[]) {

    if (argc < 4) {
        usage();
        return EXIT_FAILURE;
    }

    int count      = atoi(argv[1]);
    int iterations = atoi(argv[2]);
    InputType type;

#define is_keyword(key) (strcmp(argv[3], key) == 0)
    if (is_keyword("descending") || is_keyword("desc") || is_keyword("dsc")) {
        type = InputType::descending;
    } else if (is_keyword("ascending") || is_keyword("asc")) {
        type = InputType::ascending;
    } else if (is_keyword("random") || is_keyword("rand") || is_keyword("rnd")) {
        type = InputType::random;
    } else {
        usage();
        return EXIT_FAILURE;
    }
#undef is_keyword

    Test test(type, count, iterations);
    test.run();

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include "UIAnimatedImage.h"
#include "../../Components/Graphics/SpritesheetComponent.h"

namespace star
{
	UIAnimatedImage::UIAnimatedImage(
		const tstring & name,
		const tstring & filePath,
		const tstring & imageName,
		const tstring & spritesheet
		)
		: UIElement(name)
	{
		m_pSprite =
			new SpritesheetComponent(
			filePath,
			imageName,
			spritesheet
			);
		m_pSprite->SetHUDOptionEnabled(true);

		AddComponent(m_pSprite);
	}

	UIAnimatedImage::~UIAnimatedImage(void)
	{

	}

	void UIAnimatedImage::SetHorizontalAlignment(HorizontalAlignment alignment)
	{
		switch(alignment)
		{
			case HorizontalAlignment::Left:
				GetTransform()->SetCenterX(0);
				break;
			case HorizontalAlignment::Center:
				GetTransform()->SetCenterX(
					float32(m_pSprite->GetWidth()) / 2.0f
					);
				break;
			case HorizontalAlignment::Right:
				GetTransform()->SetCenterX(
					float32(m_pSprite->GetWidth())
					);
				break;
		}
		UIElement::SetHorizontalAlignment(alignment);
	}

	void UIAnimatedImage::SetVerticalAlignment(VerticalAlignment alignment)
	{
		switch(alignment)
		{
			case VerticalAlignment::Bottom:
				GetTransform()->SetCenterY(0);
				break;
			case VerticalAlignment::Center:
				GetTransform()->SetCenterY(
					float32(m_pSprite->GetHeight()) / 2.0f
					);
				break;
			case VerticalAlignment::Top:
				GetTransform()->SetCenterY(
					float32(m_pSprite->GetHeight())
					);
				break;
		}
		UIElement::SetVerticalAlignment(alignment);
	}

	void UIAnimatedImage::Play()
	{
		m_pSprite->Play();
	}

	void UIAnimatedImage::Restart()
	{
		m_pSprite->Restart();
	}

	void UIAnimatedImage::Pause()
	{
		m_pSprite->Pause();
	}

	void UIAnimatedImage::Stop()
	{
		m_pSprite->Stop();
	}

	void UIAnimatedImage::PushAnimation(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PushAnimation(animation, callback);
	}

	void UIAnimatedImage::PlayAnimation(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PlayAnimation(animation, callback);
	}

	void UIAnimatedImage::PlayAnimation(
		const tstring & animation,
		int32 startFrame,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PlayAnimation(animation, startFrame, callback);
	}

	bool UIAnimatedImage::PushAnimationSafe(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PushAnimationSafe(animation, callback);
	}

	bool UIAnimatedImage::PlayAnimationSafe(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PlayAnimationSafe(animation, callback);
	}

	bool UIAnimatedImage::PlayAnimationSafe(
		const tstring & animation,
		int32 startFrame,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PlayAnimationSafe(animation, startFrame, callback);
	}

	void UIAnimatedImage::PlayNextAnimation()
	{
		m_pSprite->PlayNextAnimation();
	}

	void UIAnimatedImage::PlayNextAnimation(int32 startFrame)
	{
		m_pSprite->PlayNextAnimation(startFrame);
	}

	void UIAnimatedImage::Reset()
	{
		m_pSprite->Reset();
	}

	void UIAnimatedImage::SetSpritesheet(const tstring & spritesheet)
	{
		m_pSprite->SetSpritesheet(spritesheet);
	}

	tstring UIAnimatedImage::GetCurrentAnimation() const
	{
		return m_pSprite->GetCurrentAnimation();
	}

	const tstring & UIAnimatedImage::GetcurrentSpritesheet() const
	{
		return m_pSprite->GetcurrentSpritesheet();
	}

	int32 UIAnimatedImage::GetCurrentFrame() const
	{
		return m_pSprite->GetCurrentFrame();
	}

	int32 UIAnimatedImage::GetFramesHorizontal() const
	{
		return m_pSprite->GetFramesHorizontal();
	}

	int32 UIAnimatedImage::GetFramesVertical() const
	{
		return m_pSprite->GetFramesVertical();
	}

	int32 UIAnimatedImage::GetWidth() const
	{
		return m_pSprite->GetWidth();
	}

	int32 UIAnimatedImage::GetHeight() const
	{
		return m_pSprite->GetHeight();
	}

	void UIAnimatedImage::SetCallbackAnimations(const std::function<void()> & callback)
	{
		m_pSprite->SetCallbackAnimations(callback);
	}

	bool UIAnimatedImage::IsPlaying()
	{
		return m_pSprite->IsPlaying();
	}
}<commit_msg>Hotfix previous update<commit_after>#include "UIAnimatedImage.h"
#include "../../Components/Graphics/SpritesheetComponent.h"

namespace star
{
	UIAnimatedImage::UIAnimatedImage(
		const tstring & name,
		const tstring & filePath,
		const tstring & imageName,
		const tstring & spritesheet
		)
		: UIElement(name)
	{
		m_pSprite =
			new SpritesheetComponent(
			filePath,
			imageName,
			spritesheet
			);
		m_pSprite->SetHUDOptionEnabled(true);

		AddComponent(m_pSprite);
	}

	UIAnimatedImage::~UIAnimatedImage(void)
	{

	}

	void UIAnimatedImage::SetHorizontalAlignment(HorizontalAlignment alignment)
	{
		switch(alignment)
		{
			case HorizontalAlignment::Left:
				GetTransform()->SetCenterX(0);
				break;
			case HorizontalAlignment::Center:
				GetTransform()->SetCenterX(
					float32(m_pSprite->GetWidth()) / 2.0f
					);
				break;
			case HorizontalAlignment::Right:
				GetTransform()->SetCenterX(
					float32(m_pSprite->GetWidth())
					);
				break;
		}
		UIElement::SetHorizontalAlignment(alignment);
	}

	void UIAnimatedImage::SetVerticalAlignment(VerticalAlignment alignment)
	{
		switch(alignment)
		{
			case VerticalAlignment::Bottom:
				GetTransform()->SetCenterY(0);
				break;
			case VerticalAlignment::Center:
				GetTransform()->SetCenterY(
					float32(m_pSprite->GetHeight()) / 2.0f
					);
				break;
			case VerticalAlignment::Top:
				GetTransform()->SetCenterY(
					float32(m_pSprite->GetHeight())
					);
				break;
		}
		UIElement::SetVerticalAlignment(alignment);
	}

	void UIAnimatedImage::Play()
	{
		m_pSprite->Play();
	}

	void UIAnimatedImage::Restart()
	{
		m_pSprite->Restart();
	}

	void UIAnimatedImage::Pause()
	{
		m_pSprite->Pause();
	}

	void UIAnimatedImage::Stop()
	{
		m_pSprite->Stop();
	}

	void UIAnimatedImage::PushAnimation(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PushAnimation(animation, callback);
	}

	void UIAnimatedImage::PlayAnimation(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PlayAnimation(animation, callback);
	}

	void UIAnimatedImage::PlayAnimation(
		const tstring & animation,
		int32 startFrame,
		const std::function<void()> & callback
		)
	{
		m_pSprite->PlayAnimation(animation, startFrame, callback);
	}

	bool UIAnimatedImage::PushAnimationSafe(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		return m_pSprite->PushAnimationSafe(animation, callback);
	}

	bool UIAnimatedImage::PlayAnimationSafe(
		const tstring & animation,
		const std::function<void()> & callback
		)
	{
		return m_pSprite->PlayAnimationSafe(animation, callback);
	}

	bool UIAnimatedImage::PlayAnimationSafe(
		const tstring & animation,
		int32 startFrame,
		const std::function<void()> & callback
		)
	{
		return m_pSprite->PlayAnimationSafe(animation, startFrame, callback);
	}

	void UIAnimatedImage::PlayNextAnimation()
	{
		m_pSprite->PlayNextAnimation();
	}

	void UIAnimatedImage::PlayNextAnimation(int32 startFrame)
	{
		m_pSprite->PlayNextAnimation(startFrame);
	}

	void UIAnimatedImage::Reset()
	{
		m_pSprite->Reset();
	}

	void UIAnimatedImage::SetSpritesheet(const tstring & spritesheet)
	{
		m_pSprite->SetSpritesheet(spritesheet);
	}

	tstring UIAnimatedImage::GetCurrentAnimation() const
	{
		return m_pSprite->GetCurrentAnimation();
	}

	const tstring & UIAnimatedImage::GetcurrentSpritesheet() const
	{
		return m_pSprite->GetcurrentSpritesheet();
	}

	int32 UIAnimatedImage::GetCurrentFrame() const
	{
		return m_pSprite->GetCurrentFrame();
	}

	int32 UIAnimatedImage::GetFramesHorizontal() const
	{
		return m_pSprite->GetFramesHorizontal();
	}

	int32 UIAnimatedImage::GetFramesVertical() const
	{
		return m_pSprite->GetFramesVertical();
	}

	int32 UIAnimatedImage::GetWidth() const
	{
		return m_pSprite->GetWidth();
	}

	int32 UIAnimatedImage::GetHeight() const
	{
		return m_pSprite->GetHeight();
	}

	void UIAnimatedImage::SetCallbackAnimations(const std::function<void()> & callback)
	{
		m_pSprite->SetCallbackAnimations(callback);
	}

	bool UIAnimatedImage::IsPlaying()
	{
		return m_pSprite->IsPlaying();
	}
}<|endoftext|>


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

#include <gtest/gtest.h>

#include <string>

#include <SurgSim/Physics/Actors/Actor.h>
#include <SurgSim/Math/Vector.h>

using SurgSim::Physics::Actor;
using SurgSim::Math::Vector3d;

class ActorTest : public ::testing::Test
{
public:
	void SetUp()
	{
		m_Gravity << 0.0, -9.81, 0.0;
		m_zeroGravity.setZero();
	}

	void TearDown()
	{
	}

	// Gravity vector (normal version)
	Vector3d m_Gravity;

	// Gravity vector (version without gravity)
	Vector3d m_zeroGravity;
};

TEST_F(ActorTest, ConstructorTest)
{
	ASSERT_NO_THROW({Actor actor("Actor");});
}

TEST_F(ActorTest, ResetStateTest)
{
	// Create the rigid body
	std::shared_ptr<Actor> actor = std::make_shared<Actor>("Actor");

	// Set the actor state to non default values
	actor->setIsActive(false);
	actor->setIsGravityEnabled(false);
	actor->setGravity(m_zeroGravity);

	// Reset the actor state to the default values
	actor->resetState();

	// isActive flag [default = true]
	EXPECT_TRUE(actor->isActive());
	// isGravityEnable flag [default = true]
	EXPECT_TRUE(actor->isGravityEnabled());
	// gravity vector [default = (0 -9.81 0)]
	EXPECT_EQ(m_Gravity, actor->getGravity());
}

TEST_F(ActorTest, SetGetAndDefaultValueTest)
{
	/// Create the actor
	std::shared_ptr<Actor> actor = std::make_shared<Actor>("Actor");

	/// Get/Set active flag [default = true]
	EXPECT_TRUE(actor->isActive());
	actor->setIsActive(false);
	ASSERT_FALSE(actor->isActive());
	actor->setIsActive(true);
	ASSERT_TRUE(actor->isActive());

	/// Get numDof = 0
	ASSERT_EQ(0, actor->getNumDof());

	/// Set/Get isGravityEnabled [default = true]
	EXPECT_TRUE(actor->isGravityEnabled());
	actor->setIsGravityEnabled(false);
	ASSERT_FALSE(actor->isGravityEnabled());
	actor->setIsGravityEnabled(true);
	ASSERT_TRUE(actor->isGravityEnabled());

	/// Set/Get gravity [default = (0 -9.81 0)]
	EXPECT_EQ(m_Gravity, actor->getGravity());
	actor->setGravity(m_zeroGravity);
	ASSERT_EQ(m_zeroGravity, actor->getGravity());
	actor->setGravity(m_Gravity);
	ASSERT_EQ(m_Gravity, actor->getGravity());
}
<commit_msg>Minor change (m_Gravity -> m_gravity) to follow our standard<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <gtest/gtest.h>

#include <string>

#include <SurgSim/Physics/Actors/Actor.h>
#include <SurgSim/Math/Vector.h>

using SurgSim::Physics::Actor;
using SurgSim::Math::Vector3d;

class ActorTest : public ::testing::Test
{
public:
	void SetUp()
	{
		m_gravity << 0.0, -9.81, 0.0;
		m_zeroGravity.setZero();
	}

	void TearDown()
	{
	}

	// Gravity vector (normal version)
	Vector3d m_gravity;

	// Gravity vector (version without gravity)
	Vector3d m_zeroGravity;
};

TEST_F(ActorTest, ConstructorTest)
{
	ASSERT_NO_THROW({Actor actor("Actor");});
}

TEST_F(ActorTest, ResetStateTest)
{
	// Create the rigid body
	std::shared_ptr<Actor> actor = std::make_shared<Actor>("Actor");

	// Set the actor state to non default values
	actor->setIsActive(false);
	actor->setIsGravityEnabled(false);
	actor->setGravity(m_zeroGravity);

	// Reset the actor state to the default values
	actor->resetState();

	// isActive flag [default = true]
	EXPECT_TRUE(actor->isActive());
	// isGravityEnable flag [default = true]
	EXPECT_TRUE(actor->isGravityEnabled());
	// gravity vector [default = (0 -9.81 0)]
	EXPECT_EQ(m_gravity, actor->getGravity());
}

TEST_F(ActorTest, SetGetAndDefaultValueTest)
{
	/// Create the actor
	std::shared_ptr<Actor> actor = std::make_shared<Actor>("Actor");

	/// Get/Set active flag [default = true]
	EXPECT_TRUE(actor->isActive());
	actor->setIsActive(false);
	ASSERT_FALSE(actor->isActive());
	actor->setIsActive(true);
	ASSERT_TRUE(actor->isActive());

	/// Get numDof = 0
	ASSERT_EQ(0, actor->getNumDof());

	/// Set/Get isGravityEnabled [default = true]
	EXPECT_TRUE(actor->isGravityEnabled());
	actor->setIsGravityEnabled(false);
	ASSERT_FALSE(actor->isGravityEnabled());
	actor->setIsGravityEnabled(true);
	ASSERT_TRUE(actor->isGravityEnabled());

	/// Set/Get gravity [default = (0 -9.81 0)]
	EXPECT_EQ(m_gravity, actor->getGravity());
	actor->setGravity(m_zeroGravity);
	ASSERT_EQ(m_zeroGravity, actor->getGravity());
	actor->setGravity(m_gravity);
	ASSERT_EQ(m_gravity, actor->getGravity());
}
<|endoftext|>


<commit_before>#pragma once

#include <stdexcept>
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>

#include "Utility.hpp"

/*
    TrimChars Policy
*/

template<char... TrimChar>
struct TrimChars
{
public:

    static void trim(std::string& line)
    {
        auto found_iter = std::remove_if(line.begin(), line.end(),
            [](char c)
        {
            return isTrimChar(c, TrimChar...);
        });

        found_iter = line.erase(found_iter, line.end());
    }

private:

    static bool isTrimChar(char c)
    {
        return false;
    }

    template<class... OtherTrimChar>
    static bool isTrimChar(char c, char trim_char, OtherTrimChar... other_trim_char)
    {
        return c == trim_char || isTrimChar(c, other_trim_char...);
    }

};

/*
    QuoteEscape Policy
*/

template<char sep>
struct NoQuoteEscape
{
    static std::string split(std::string& line)
    {
        std::string result;

        std::size_t found_pos = line.find_first_of(sep, 0);

        if (found_pos == std::string::npos)
        {
            result = line;

            line.clear();
        }
        else
        {
            result = line.substr(0, found_pos);

            line = line.substr(found_pos + 1);
        }

        return result;
    }

    static void unescape(std::string& line)
    {
    }
};

template<char sep, char quote>
struct DoubleQuoteEscape
{
    static std::string split(std::string& line)
    {
        std::string result;

        std::size_t sep_pos = line.find_first_of(sep, 0);
        std::size_t quote_start_pos = line.find_first_of(quote, 0);
        std::size_t quote_end_pos = line.find_first_of(quote, quote_start_pos + 1);

        if (quote_start_pos != std::string::npos &&
            ((quote_end_pos != std::string::npos && sep_pos < quote_end_pos) ||
                quote_end_pos == std::string::npos))
        {
            throw std::runtime_error("No ending escape string was found!");
        }

        if (sep_pos == std::string::npos)
        {
            result = line;

            line.clear();
        }
        else
        {
            result = line.substr(0, sep_pos);

            line = line.substr(sep_pos + 1);
        }

        return result;
    }

    static void unescape(std::string& line)
    {
        for (std::size_t next = line.find(quote);
            next != std::string::npos;
            next = line.find(quote, next)
            )
        {
            line.erase(next, 1);
            next += 1;
        }
    }
};

/*
    Comment Policy
*/

struct NoComment
{
    static bool isComment(const std::string& line)
    {
        return false;
    }
};

struct EmptyLineComment
{
    static bool isComment(const std::string& line)
    {
        if (line.empty())
        {
            return true;
        }

        std::string buffer = line;

        auto found_iter = std::remove(buffer.begin(), buffer.end(), ' ');
        found_iter = buffer.erase(found_iter, buffer.end());

        found_iter = std::remove(buffer.begin(), buffer.end(), '\t');
        found_iter = buffer.erase(found_iter, buffer.end());

        if (found_iter != buffer.end())
        {
            return true;
        }

        return false;
    }
};

/*
    Helper
*/

namespace detail
{
    template<class T>
    inline void parse(const std::string& cell, T& object)
    {
        std::istringstream iss(cell);

        iss >> object;
    }

    template<>
    inline void parse(const std::string& cell, bool& object)
    {
        std::string buffer = cell;

        std::for_each(buffer.begin(), buffer.end(),
            [](char& c)
        {
            c = std::tolower(c);
        });

        std::istringstream iss(buffer);
        iss.imbue({ iss.getloc(), new boolyesno() });

        iss >> std::boolalpha >> object;
    }

    template<>
    inline void parse(const std::string& cell, std::string& object)
    {
        object.assign(cell.begin(), cell.end());
    }
}

/*
    CSV Parser
*/

template
<
    unsigned int column_num,
    class CommentPolicy = EmptyLineComment,
    class QuotePolicy = NoQuoteEscape<','>,
    class TrimPolicy = TrimChars<'\t'>
>
class CSVParser
{
public:

    enum class IgnoreType : std::size_t
    {
        Invalid = 0,
        NoColumn,
        ExtraColumn,
        MissingColumn,
        MaxCount
    };

    CSVParser(const std::string& file_name)
    {
        header_columns.resize(column_num);

        file.open(file_name);

        if (!file)
        {
            throw std::runtime_error("CSV file not found!");
        }
    }

    template<class ...ColumnTypes>
    void readHeader(IgnoreType ignore_column, ColumnTypes&... header_columns)
    {
        constexpr std::size_t column_count = sizeof...(ColumnTypes);

        static_assert(column_count >= column_num, "Not enough columns specified");
        static_assert(column_count <= column_num, "Too many columns specified");

        setHeaderColumns(header_columns...);

        std::string line;

        do
        {
            std::getline(file, line);

            if (line.empty())
            {
                throw std::runtime_error("Header row is missing!");
            }
        } while (CommentPolicy::isComment(line));

        std::vector<bool> found_columns(column_count);
        std::fill(found_columns.begin(), found_columns.end(), false);

        while (!line.empty())
        {
            std::string column = QuotePolicy::split(line);

            TrimPolicy::trim(column);
            QuotePolicy::unescape(column);

            auto column_iter = std::find(header_columns.begin(), header_columns.end(), column);

            if (column_iter == header_columns.end())
            {
                if (ignore_column == IgnoreType::ExtraColumn)
                {
                    column_skip.push_back(-1);
                }
                else
                {
                    throw std::runtime_error("Column not found!");
                }
            }

            std::size_t index = std::distance(header_columns.begin(), column_iter);

            found_columns[index] = true;

            column_skip.push_back(index);
        }

        if (ignore_column != IgnoreType::MissingColumn)
        {
            std::for_each(found_columns.begin(), found_columns.end(),
                [](bool found_column)
            {
                if (!found_column)
                {
                    throw std::runtime_error("Missing column in header file");
                }
            });
        }
    }

    template<class ...ColumnTypes>
    bool readRow(ColumnTypes&... column_types)
    {
        constexpr std::size_t column_cout = sizeof...(ColumnTypes);

        static_assert(column_cout >= column_num, "Not enough columns specified");
        static_assert(column_cout <= column_num, "Too many columns specified");

        std::string line;

        do
        {
            std::getline(file, line);

            if (line.empty())
            {
                return false;
            }
        } while (CommentPolicy::isComment(line));

        std::for_each(column_skip.begin(), column_skip.end(),
            [&](std::int32_t skip_column)
        {
            std::string cell = QuotePolicy::split(line);

            if (skip_column < 0)
            {
                row_columns.push_back(std::string());
            }
            else
            {
                TrimPolicy::trim(cell);
                QuotePolicy::unescape(cell);

                row_columns.push_back(cell);
            }
        });

        setRowColumns(0, column_types...);

        row_columns.clear();

        return true;
    }

private:

    std::ifstream file;

    std::vector<std::string> header_columns;

    std::vector<std::int32_t> column_skip;

    std::vector<std::string> row_columns;

    template<class ...ColumnTypes>
    void setHeaderColumns(const std::string& column_name, ColumnTypes... column_types)
    {
        constexpr std::size_t column_left = sizeof...(ColumnTypes);

        header_columns[column_num - column_left - 1] = std::move(column_name);

        setHeaderColumns(std::forward<ColumnTypes>(column_types)...);
    }

    void setHeaderColumns()
    {
    }

    template<class T, class ...ColumnTypes>
    void setRowColumns(std::size_t index, T& object, ColumnTypes&... column_types)
    {
        std::string cell = row_columns.at(index);

        if (column_skip.at(index) != -1)
        {
            detail::parse<T>(cell, object);
        }

        setRowColumns(index + 1, column_types...);
    }

    void setRowColumns(std::size_t index)
    {
    }

};
<commit_msg>Fix bug where parameter name clashed with the object's variable name.<commit_after>#pragma once

#include <stdexcept>
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>

#include "Utility.hpp"

/*
    TrimChars Policy
*/

template<char... TrimChar>
struct TrimChars
{
public:

    static void trim(std::string& line)
    {
        auto found_iter = std::remove_if(line.begin(), line.end(),
            [](char c)
        {
            return isTrimChar(c, TrimChar...);
        });

        found_iter = line.erase(found_iter, line.end());
    }

private:

    static bool isTrimChar(char c)
    {
        return false;
    }

    template<class... OtherTrimChar>
    static bool isTrimChar(char c, char trim_char, OtherTrimChar... other_trim_char)
    {
        return c == trim_char || isTrimChar(c, other_trim_char...);
    }

};

/*
    QuoteEscape Policy
*/

template<char sep>
struct NoQuoteEscape
{
    static std::string split(std::string& line)
    {
        std::string result;

        std::size_t found_pos = line.find_first_of(sep, 0);

        if (found_pos == std::string::npos)
        {
            result = line;

            line.clear();
        }
        else
        {
            result = line.substr(0, found_pos);

            line = line.substr(found_pos + 1);
        }

        return result;
    }

    static void unescape(std::string& line)
    {
    }
};

template<char sep, char quote>
struct DoubleQuoteEscape
{
    static std::string split(std::string& line)
    {
        std::string result;

        std::size_t sep_pos = line.find_first_of(sep, 0);
        std::size_t quote_start_pos = line.find_first_of(quote, 0);
        std::size_t quote_end_pos = line.find_first_of(quote, quote_start_pos + 1);

        if (quote_start_pos != std::string::npos &&
            ((quote_end_pos != std::string::npos && sep_pos < quote_end_pos) ||
                quote_end_pos == std::string::npos))
        {
            throw std::runtime_error("No ending escape string was found!");
        }

        if (sep_pos == std::string::npos)
        {
            result = line;

            line.clear();
        }
        else
        {
            result = line.substr(0, sep_pos);

            line = line.substr(sep_pos + 1);
        }

        return result;
    }

    static void unescape(std::string& line)
    {
        for (std::size_t next = line.find(quote);
            next != std::string::npos;
            next = line.find(quote, next)
            )
        {
            line.erase(next, 1);
            next += 1;
        }
    }
};

/*
    Comment Policy
*/

struct NoComment
{
    static bool isComment(const std::string& line)
    {
        return false;
    }
};

struct EmptyLineComment
{
    static bool isComment(const std::string& line)
    {
        if (line.empty())
        {
            return true;
        }

        std::string buffer = line;

        auto found_iter = std::remove(buffer.begin(), buffer.end(), ' ');
        found_iter = buffer.erase(found_iter, buffer.end());

        found_iter = std::remove(buffer.begin(), buffer.end(), '\t');
        found_iter = buffer.erase(found_iter, buffer.end());

        if (found_iter != buffer.end())
        {
            return true;
        }

        return false;
    }
};

/*
    Helper
*/

namespace detail
{
    template<class T>
    inline void parse(const std::string& cell, T& object)
    {
        std::istringstream iss(cell);

        iss >> object;
    }

    template<>
    inline void parse(const std::string& cell, bool& object)
    {
        std::string buffer = cell;

        std::for_each(buffer.begin(), buffer.end(),
            [](char& c)
        {
            c = std::tolower(c);
        });

        std::istringstream iss(buffer);
        iss.imbue({ iss.getloc(), new boolyesno() });

        iss >> std::boolalpha >> object;
    }

    template<>
    inline void parse(const std::string& cell, std::string& object)
    {
        object.assign(cell.begin(), cell.end());
    }
}

/*
    CSV Parser
*/

template
<
    unsigned int column_num,
    class CommentPolicy = EmptyLineComment,
    class QuotePolicy = NoQuoteEscape<','>,
    class TrimPolicy = TrimChars<'\t'>
>
class CSVParser
{
public:

    enum class IgnoreType : std::size_t
    {
        Invalid = 0,
        NoColumn,
        ExtraColumn,
        MissingColumn,
        MaxCount
    };

    CSVParser(const std::string& file_name)
    {
        header_columns.resize(column_num);

        file.open(file_name);

        if (!file)
        {
            throw std::runtime_error("CSV file not found!");
        }
    }

    template<class ...ColumnTypes>
    void readHeader(IgnoreType ignore_column, ColumnTypes&... header_columns)
    {
        constexpr std::size_t column_count = sizeof...(ColumnTypes);

        static_assert(column_count >= column_num, "Not enough columns specified");
        static_assert(column_count <= column_num, "Too many columns specified");

        setHeaderColumns(header_columns...);

        std::string line;

        do
        {
            std::getline(file, line);

            if (line.empty())
            {
                throw std::runtime_error("Header row is missing!");
            }
        } while (CommentPolicy::isComment(line));

        std::vector<bool> found_columns(column_count);
        std::fill(found_columns.begin(), found_columns.end(), false);

        while (!line.empty())
        {
            std::string column = QuotePolicy::split(line);

            TrimPolicy::trim(column);
            QuotePolicy::unescape(column);

            auto column_iter = std::find(this->header_columns.begin(), this->header_columns.end(), column);

            if (column_iter == this->header_columns.end())
            {
                if (ignore_column == IgnoreType::ExtraColumn)
                {
                    column_skip.push_back(-1);
                }
                else
                {
                    throw std::runtime_error("Column not found!");
                }
            }

            std::size_t index = std::distance(this->header_columns.begin(), column_iter);

            found_columns[index] = true;

            column_skip.push_back(index);
        }

        if (ignore_column != IgnoreType::MissingColumn)
        {
            std::for_each(found_columns.begin(), found_columns.end(),
                [](bool found_column)
            {
                if (!found_column)
                {
                    throw std::runtime_error("Missing column in header file");
                }
            });
        }
    }

    template<class ...ColumnTypes>
    bool readRow(ColumnTypes&... column_types)
    {
        constexpr std::size_t column_cout = sizeof...(ColumnTypes);

        static_assert(column_cout >= column_num, "Not enough columns specified");
        static_assert(column_cout <= column_num, "Too many columns specified");

        std::string line;

        do
        {
            std::getline(file, line);

            if (line.empty())
            {
                return false;
            }
        } while (CommentPolicy::isComment(line));

        std::for_each(column_skip.begin(), column_skip.end(),
            [&](std::int32_t skip_column)
        {
            std::string cell = QuotePolicy::split(line);

            if (skip_column < 0)
            {
                row_columns.push_back(std::string());
            }
            else
            {
                TrimPolicy::trim(cell);
                QuotePolicy::unescape(cell);

                row_columns.push_back(cell);
            }
        });

        setRowColumns(0, column_types...);

        row_columns.clear();

        return true;
    }

private:

    std::ifstream file;

    std::vector<std::string> header_columns;

    std::vector<std::int32_t> column_skip;

    std::vector<std::string> row_columns;

    template<class ...ColumnTypes>
    void setHeaderColumns(const std::string& column_name, ColumnTypes... column_types)
    {
        constexpr std::size_t column_left = sizeof...(ColumnTypes);

        header_columns[column_num - column_left - 1] = std::move(column_name);

        setHeaderColumns(std::forward<ColumnTypes>(column_types)...);
    }

    void setHeaderColumns()
    {
    }

    template<class T, class ...ColumnTypes>
    void setRowColumns(std::size_t index, T& object, ColumnTypes&... column_types)
    {
        std::string cell = row_columns.at(index);

        if (column_skip.at(index) != -1)
        {
            detail::parse<T>(cell, object);
        }

        setRowColumns(index + 1, column_types...);
    }

    void setRowColumns(std::size_t index)
    {
    }

};
<|endoftext|>