text:
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <set>
#include <boost/assign/list_of.hpp>
#include <boost/assign/ptr_map_inserter.hpp>
#include <tier1/KeyValues.h>

#include "weapon_attributes.h"

typedef std::map<std::string, KeyValues*> kv_map_t;

bool is_ascii(char c1, char c2)
{
    return (c1 < 128 && c2 == 0);
}

const char* load_file_to_buffer(const char* filename)
{
    std::ifstream ifs(filename);
    if (!ifs)
    {
        std::stringstream ss;
        ss << "Failed to open file \"" << filename << "\"";
        throw std::runtime_error(ss.str());
    }
    ifs.seekg(0, std::ios::end);
    int length = ifs.tellg();
    ifs.seekg(0, std::ios::beg);

    char* buffer = new char[length+1];
    ifs.read(buffer, length);

    // hack for UTF-16
    if (buffer[0] == '\377' && buffer[1] == '\376') // LE
    {
        // count number of ascii characters
        int ascii_length = 0;
        for (int i = 2; i < length; i += 2)
        {
            // throw away every nonascii character
            if (is_ascii(buffer[i], buffer[i+1]))
                ++ascii_length;
        }

        // copy ascii characters to new buffer
        char* ascii_buffer = new char[ascii_length+1];
        int j = 0;
        for (int i = 2; i < length; i += 2)
        {
            // throw away every nonascii character
            if (is_ascii(buffer[i], buffer[i+1]))
                ascii_buffer[j++] = buffer[i];
        }
        delete[] buffer;
        ascii_buffer[j] = '\0';
        return ascii_buffer;
    }
    else
    {
        buffer[length] = '\0';
        return buffer;
    }
}

KeyValues* load_key_values(const char* name, const char* filename)
{
    KeyValues* kv = new KeyValues(name);
    kv->UsesEscapeSequences(true);

    const char* buffer = load_file_to_buffer(filename);
    bool result = kv->LoadFromBuffer(name, buffer);
    delete[] buffer;

    if (!result)
    {
        std::stringstream ss;
        ss << "Failed to load KeyValues \"" << filename << "\"";
        throw std::runtime_error(ss.str());
    }

    return kv;
}

std::pair<std::string, std::string> split_path(const char* filename)
{
    std::string filename_string(filename);
    unsigned int pos = filename_string.find_last_of("/");

    std::pair<std::string, std::string> path;
    if (pos > filename_string.length())
    {
        path.first = ".";
        path.second = filename_string;
    }
    else
    {
        path.first = filename_string.substr(0, pos);
        path.second = filename_string.substr(pos + 1);
    }
    return path;
}

kv_map_t load_manifest(const char* filename)
{
    // create and load manifest KeyValues
    static const char manifest_name[] = "weapon_manifest";
    KeyValues* manifest = load_key_values(manifest_name, filename);

    // build map of files named in manifest
    kv_map_t manifest_files;
    std::pair<std::string, std::string> path = split_path(filename);
    for (KeyValues* file = manifest->GetFirstSubKey(); file != NULL; file = file->GetNextKey())
    {
        // check for file key
        if (Q_stricmp(file->GetName(), "file") != 0)
        {
            std::cerr << "Warning: unrecognized key \"" << file->GetName() <<
                "\" in weapons manifest \"" << filename << "\"" << std::endl;
            continue;
        }

        // build script filename
        std::stringstream script_filename_ss;
        script_filename_ss << path.first << "/" << file->GetString() << ".txt";

        // create and load KeyValues
        KeyValues* script = load_key_values(file->GetString(), script_filename_ss.str().c_str());

        // check if WeaponType is one that we care about
        typedef std::set<std::string> item_type_t;
        static item_type_t item_types = boost::assign::list_of ("Pistol") ("Rifle") ("Shotgun") ("SniperRifle") ("Machinegun") ("SubMachinegun");
        item_type_t::const_iterator i = item_types.find(script->GetString("WeaponType"));
        if (i == item_types.end())
            continue;

        manifest_files.insert(kv_map_t::value_type(file->GetString(), script));
    }
    return manifest_files;
}

void chain_to_prefabs(KeyValues* items, KeyValues* prefabs)
{
    for (KeyValues* item = items->GetFirstSubKey(); item != NULL; item = item->GetNextKey())
    {
        const char* prefab_name = item->GetString("prefab");
        if (prefab_name && strlen(prefab_name) > 0)
        {
            KeyValues* prefab = prefabs->FindKey(prefab_name);
            if (prefab == NULL)
            {
                std::stringstream ss;
                ss << "Item \"" << item->GetName() << "\" has nonexistent prefab \"" << prefab_name << "\"";
                throw std::runtime_error(ss.str());
            }
            // chain attributes first
            KeyValues* item_attrs = item->FindKey("attributes", true);
            KeyValues* prefab_attrs = prefab->FindKey("attributes", true);
            item_attrs->ChainKeyValue(item);
            item->ChainKeyValue(prefab_attrs);
            prefab_attrs->ChainKeyValue(prefab);

            // then chain base
            //item->ChainKeyValue(prefab);
        }
    }
}

kv_map_t load_items_script(const char* filename)
{
    static const char items_script_name[] = "items_game";
    KeyValues* items_script = load_key_values(items_script_name, filename);

    KeyValues* prefabs = items_script->FindKey("prefabs");
    if (prefabs == NULL)
    {
        std::stringstream ss;
        ss << "Failed to find prefabs in items script \"" << filename << "\"";
        throw std::runtime_error(ss.str());
    }
    chain_to_prefabs(prefabs, prefabs);

    KeyValues* items = items_script->FindKey("items");
    if (items == NULL)
    {
        std::stringstream ss;
        ss << "Failed to find items in items script \"" << filename << "\"";
        throw std::runtime_error(ss.str());
    }
    chain_to_prefabs(items, prefabs);

    // build list of items from script
    kv_map_t item_map;
    typedef std::set<std::string> item_slot_t;
    static item_slot_t item_slots = boost::assign::list_of ("secondary") ("smg") ("rifle") ("heavy");
    for (KeyValues* item = items->GetFirstSubKey(); item != NULL; item = item->GetNextKey())
    {
        // check if item_slot is one that we care about
        item_slot_t::const_iterator i = item_slots.find(item->GetString("item_slot"));
        if (i == item_slots.end())
            continue;

        item_map.insert(kv_map_t::value_type(item->GetString("name"), item));
    }
    return item_map;
}

const char* trim_leading_pound(const char* s)
{
    if (s[0] == '#')
        return &s[1];
    else
        return s;
}

WeaponAttributesDatabase::WeaponAttributesDatabase(const char* manifest_filename, const char* items_script_filename, const char* resource_filename)
{
    kv_map_t item_classes = load_manifest(manifest_filename);
    kv_map_t items;
    try
    {
        items = load_items_script(items_script_filename);
    }
    catch (std::runtime_error e)
    {
        std::cerr << "Failed to load items script, using item classes only" << std::endl;
    }

    // create and load resource KeyValues
    static const char resources_name[] = "resources";
    KeyValues* resources = load_key_values(resources_name, resource_filename)->FindKey("Tokens");
    if (!resources)
    {
        std::stringstream ss;
        ss << "Failed to find Tokens in \"" << resource_filename << "\"";
        throw std::runtime_error(ss.str());
    }

    // older version don't use the items_script
    if (items.size() > 0)
    {
        // build ItemAttributes from above
        for (kv_map_t::iterator i = items.begin(), e = items.end(); i != e; ++i)
        {
            kv_map_t::iterator item_class = item_classes.find(i->second->GetString("item_class"));
            if (item_class == item_classes.end())
            {
                std::stringstream ss;
                ss << "Failed to find item_class \"" << i->second->GetString("item_class") << "\" for item \"" << i->first << "\"";
                throw std::runtime_error(ss.str());
            }

            // parse item name and convert via resource file
            std::string item_name = item_class->second->GetString("printname", "Unknown");
            item_name = i->second->GetString("item_name", item_name.c_str());
            item_name = resources->GetString(trim_leading_pound(item_name.c_str()), item_name.c_str());

            // parse item type and convert via resource file
            std::string item_type = item_class->second->GetString("WeaponType", "Unknown");
            item_type = i->second->GetString("item_type_name", item_type.c_str());
            item_type = resources->GetString(trim_leading_pound(item_type.c_str()), item_type.c_str());

            boost::assign::ptr_map_insert<WeaponAttributes>( weapon_attrs_ )( item_name, item_class->second, i->second, item_type );
        }
    }
    else
    {
        // build ItemAttributes from above
        for (kv_map_t::iterator i = item_classes.begin(), e = item_classes.end(); i != e; ++i)
        {
            // parse item name and convert via resource file
            std::string item_name = i->second->GetString("printname", "Unknown");
            item_name = resources->GetString(trim_leading_pound(item_name.c_str()), item_name.c_str());

            // parse item type and convert via resource file
            std::string item_type = i->second->GetString("WeaponType", "Unknown");
            item_type = resources->GetString(trim_leading_pound(item_type.c_str()), item_type.c_str());

            boost::assign::ptr_map_insert<WeaponAttributes>( weapon_attrs_ )( item_name, i->second, item_type );
        }
    }
}

const WeaponAttributes* WeaponAttributesDatabase::get(const char* weapon_name) const
{
    weapon_attrs_t::const_iterator i = weapon_attrs_.find(weapon_name);
    if (i != weapon_attrs_.end())
        return i->second;
    else
        return NULL;
}

void WeaponAttributesDatabase::write_json(const char* filename, const char* version) const
{
    std::ofstream json_file(filename);
    if (!json_file)
    {
        std::stringstream ss;
        ss << "Failed to open file for output: \"" << filename << "\"";
        throw std::runtime_error(ss.str());
    }

    std::string indent = "    ";
    json_file << "{" << std::endl;

    // write version
    json_file << indent << "\"version\" : \"" << version << "\"," << std::endl;

    // write weapon data
    json_file << indent << "\"weapons\" : [";
    bool first_entry = true;
    for (weapon_attrs_t::const_iterator i = weapon_attrs_.begin(), e = weapon_attrs_.end(); i != e; ++i)
    {
        if (!first_entry)
            json_file << ",";
        else
            first_entry = false;
        json_file << std::endl;
        json_file << indent << indent << "{" << std::endl;
        json_file << indent << indent << indent << "\"name\" : \"" << i->first << "\"," << std::endl;
        json_file << indent << indent << indent << "\"type\" : \"" << i->second->item_type << "\"," << std::endl;
        json_file << indent << indent << indent << "\"price\" : " << i->second->price << "," << std::endl;
        json_file << indent << indent << indent << "\"max_player_speed\" : " << i->second->max_player_speed << "," << std::endl;
        json_file << indent << indent << indent << "\"max_player_speed_alt\" : " << i->second->max_player_speed_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"flinch_velocity_modifier_large\" : " << i->second->flinch_velocity_mod_large << "," << std::endl;
        json_file << indent << indent << indent << "\"flinch_velocity_modifier_small\" : " << i->second->flinch_velocity_mod_small << "," << std::endl;
        json_file << indent << indent << indent << "\"penetration\" : " << i->second->penetration << "," << std::endl;
        json_file << indent << indent << indent << "\"damage\" : " << i->second->damage << "," << std::endl;
        json_file << indent << indent << indent << "\"range\" : " << i->second->range << "," << std::endl;
        json_file << indent << indent << indent << "\"range_modifier\" : " << i->second->range_modifier << "," << std::endl;
        json_file << indent << indent << indent << "\"recovery_time_crouch\" : " << i->second->recovery_time_crouch << "," << std::endl;
        json_file << indent << indent << indent << "\"recovery_time_stand\" : " << i->second->recovery_time_stand << "," << std::endl;
        json_file << indent << indent << indent << "\"bullets\" : " << i->second->bullets << "," << std::endl;
        json_file << indent << indent << indent << "\"cycle_time\" : " << i->second->cycle_time << "," << std::endl;
        json_file << indent << indent << indent << "\"cycle_time_alt\" : " << i->second->cycle_time_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"spread\" : " << i->second->spread << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_crouch\" : " << i->second->inaccuracy_crouch << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_stand\" : " << i->second->inaccuracy_stand << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_jump\" : " << i->second->inaccuracy_jump << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_land\" : " << i->second->inaccuracy_land << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_ladder\" : " << i->second->inaccuracy_ladder << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_fire\" : " << i->second->inaccuracy_fire << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_move\" : " << i->second->inaccuracy_move << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_crouch_alt\" : " << i->second->inaccuracy_crouch_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_stand_alt\" : " << i->second->inaccuracy_stand_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_jump_alt\" : " << i->second->inaccuracy_jump_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_land_alt\" : " << i->second->inaccuracy_land_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_ladder_alt\" : " << i->second->inaccuracy_ladder_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_fire_alt\" : " << i->second->inaccuracy_fire_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"inaccuracy_move_alt\" : " << i->second->inaccuracy_move_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"spread_alt\" : " << i->second->spread_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_angle\" : " << i->second->recoil_angle << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_angle_variance\" : " << i->second->recoil_angle_variance << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_magnitude\" : " << i->second->recoil_magnitude << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_magnitude_variance\" : " << i->second->recoil_magnitude_variance << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_angle_alt\" : " << i->second->recoil_angle_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_angle_variance_alt\" : " << i->second->recoil_angle_variance_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_magnitude_alt\" : " << i->second->recoil_magnitude_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_magnitude_variance_alt\" : " << i->second->recoil_magnitude_variance_alt << "," << std::endl;
        json_file << indent << indent << indent << "\"recoil_seed\" : " << i->second->recoil_seed << "," << std::endl;
        json_file << indent << indent << indent << "\"full_auto\" : " <<
            (i->second->full_auto ? "true" : "false") << "," << std::endl;
        json_file << indent << indent << indent << "\"clip_size\" : " << i->second->clip_size << "," << std::endl;
        json_file << indent << indent << indent << "\"armor_ratio\" : " << i->second->armor_ratio << "," << std::endl;
        json_file << indent << indent << indent << "\"model_world\" : \"" << i->second->model_world << "\"," << std::endl;
        json_file << indent << indent << indent << "\"model_player\" : \"" << i->second->model_player << "\"" << std::endl;
        json_file << indent << indent << "}";
    }

    json_file << std::endl << indent << "]" << std::endl;
    json_file << "}" << std::endl;
}

WeaponAttributes::WeaponAttributes(KeyValues* item_class, const std::string& item_type):
    item_type(item_type)
{
    load_item_class(item_class);
}

WeaponAttributes::WeaponAttributes(KeyValues* item_class, KeyValues* weapon_attrs, const std::string& item_type):
    item_type(item_type)
{
    load_item_class(item_class);
    load_weapon_attrs(weapon_attrs);
}

void WeaponAttributes::load_item_class(KeyValues* item_class)
{
    price                          = item_class->GetInt("WeaponPrice", 0x0FFFFFFFF);
    max_player_speed               = item_class->GetInt("MaxPlayerSpeed", 1);
    max_player_speed_alt           = item_class->GetInt("MaxPlayerSpeedAlt", max_player_speed);
    flinch_velocity_mod_large      = item_class->GetString("FlinchVelocityModifierLarge", "1.0");
    flinch_velocity_mod_small      = item_class->GetString("FlinchVelocityModifierSmall", "1.0");
    penetration                    = item_class->GetInt("Penetration", 1);
    damage                         = item_class->GetInt("Damage", 42);
    range                          = item_class->GetString("Range", "8192.0");
    range_modifier                 = item_class->GetString("RangeModifier", "0.98");
    bullets                        = item_class->GetInt("Bullets", 1);
    cycle_time                     = item_class->GetString("CycleTime", "0.15");
    cycle_time_alt                 = item_class->GetString("CycleTimeAlt", "0.3");
    spread                         = item_class->GetString("Spread", "0.0");
    inaccuracy_crouch              = item_class->GetString("InaccuracyCrouch", "0.0");
    inaccuracy_stand               = item_class->GetString("InaccuracyStand", "0.0");
    inaccuracy_jump                = item_class->GetString("InaccuracyJump", "0.0");
    inaccuracy_land                = item_class->GetString("InaccuracyLand", "0.0");
    inaccuracy_ladder              = item_class->GetString("InaccuracyLadder", "0.0");
    inaccuracy_fire                = item_class->GetString("InaccuracyFire", "0.0");
    inaccuracy_move                = item_class->GetString("InaccuracyMove", "0.0");
    spread_alt                     = item_class->GetString("SpreadAlt", spread.c_str());
    inaccuracy_crouch_alt          = item_class->GetString("InaccuracyCrouchAlt", inaccuracy_crouch.c_str());
    inaccuracy_stand_alt           = item_class->GetString("InaccuracyStandAlt", inaccuracy_stand.c_str());
    inaccuracy_jump_alt            = item_class->GetString("InaccuracyJumpAlt", inaccuracy_jump.c_str());
    inaccuracy_land_alt            = item_class->GetString("InaccuracyLandAlt", inaccuracy_land.c_str());
    inaccuracy_ladder_alt          = item_class->GetString("InaccuracyLadderAlt", inaccuracy_ladder.c_str());
    inaccuracy_fire_alt            = item_class->GetString("InaccuracyFireAlt", inaccuracy_fire.c_str());
    inaccuracy_move_alt            = item_class->GetString("InaccuracyMoveAlt", inaccuracy_move.c_str());
    recoil_angle                   = item_class->GetString("RecoilAngle", "0.0");
    recoil_angle_variance          = item_class->GetString("RecoilAngleVariance", "0.0");
    recoil_magnitude               = item_class->GetString("RecoilMagnitude", "0.0");
    recoil_magnitude_variance      = item_class->GetString("RecoilMagnitudeVariance", "0.0");
    recoil_angle_alt               = item_class->GetString("RecoilAngleAlt", recoil_angle.c_str());
    recoil_angle_variance_alt      = item_class->GetString("RecoilAngleVarianceAlt", recoil_angle_variance.c_str());
    recoil_magnitude_alt           = item_class->GetString("RecoilMagnitudeAlt", recoil_magnitude.c_str());
    recoil_magnitude_variance_alt  = item_class->GetString("RecoilMagnitudeVarianceAlt", recoil_magnitude_variance.c_str());
    recoil_seed                    = item_class->GetInt("RecoilSeed", 0);
    inaccuracy_reload              = item_class->GetString("InaccuracyReload", "0.0");
    inaccuracy_alt_switch          = item_class->GetString("InaccuracyAltSwitch", "0.0");
    recovery_time_crouch           = item_class->GetString("RecoveryTimeCrouch", "1.0");
    recovery_time_stand            = item_class->GetString("RecoveryTimeStand", "1.0");
    full_auto                      = item_class->GetInt("FullAuto", 0);
    clip_size                      = item_class->GetInt("clip_size", 1);
    armor_ratio                    = item_class->GetString("WeaponArmorRatio", "1.0");
    model_world                    = item_class->GetString("viewmodel", "");
    model_player                   = item_class->GetString("playermodel", "");
}

void WeaponAttributes::load_weapon_attrs(KeyValues* weapon_attrs)
{
    // apply attributes from weapon_attrs
    KeyValues* attrs = weapon_attrs->FindKey("attributes");
    if (attrs == NULL)
    {
        std::stringstream ss;
        ss << "No attributes found for item \"" << weapon_attrs->GetString("name") << "\"";
        throw std::runtime_error(ss.str());
    }

    price                          = attrs->GetInt("in game price", price);
    max_player_speed               = attrs->GetInt("max player speed", max_player_speed);
    max_player_speed_alt           = attrs->GetInt("max player speed alt", max_player_speed_alt);
    flinch_velocity_mod_large      = attrs->GetString("flinch velocity modifier large", flinch_velocity_mod_large.c_str());
    flinch_velocity_mod_small      = attrs->GetString("flinch velocity modifier small", flinch_velocity_mod_small.c_str());
    penetration                    = attrs->GetInt("Penetration", penetration);
    damage                         = attrs->GetInt("Damage", damage);
    range                          = attrs->GetString("Range", range.c_str());
    range_modifier                 = attrs->GetString("range modifier", range_modifier.c_str());
    bullets                        = attrs->GetInt("Bullets", bullets);
    cycle_time                     = attrs->GetString("CycleTime", cycle_time.c_str());
    cycle_time_alt                 = attrs->GetString("cycletime alt", cycle_time_alt.c_str());
    spread                         = attrs->GetString("Spread", spread.c_str());
    inaccuracy_crouch              = attrs->GetString("inaccuracy crouch", inaccuracy_crouch.c_str());
    inaccuracy_stand               = attrs->GetString("inaccuracy stand", inaccuracy_stand.c_str());
    inaccuracy_jump                = attrs->GetString("inaccuracy jump", inaccuracy_jump.c_str());
    inaccuracy_land                = attrs->GetString("inaccuracy land", inaccuracy_land.c_str());
    inaccuracy_ladder              = attrs->GetString("inaccuracy ladder", inaccuracy_ladder.c_str());
    inaccuracy_fire                = attrs->GetString("inaccuracy fire", inaccuracy_fire.c_str());
    inaccuracy_move                = attrs->GetString("inaccuracy move", inaccuracy_move.c_str());
    spread_alt                     = attrs->GetString("spread alt", spread_alt.c_str());
    inaccuracy_crouch_alt          = attrs->GetString("inaccuracy crouch alt", inaccuracy_crouch_alt.c_str());
    inaccuracy_stand_alt           = attrs->GetString("inaccuracy stand alt", inaccuracy_stand_alt.c_str());
    inaccuracy_jump_alt            = attrs->GetString("inaccuracy jump alt", inaccuracy_jump_alt.c_str());
    inaccuracy_land_alt            = attrs->GetString("inaccuracy land alt", inaccuracy_land_alt.c_str());
    inaccuracy_ladder_alt          = attrs->GetString("inaccuracy ladder alt", inaccuracy_ladder_alt.c_str());
    inaccuracy_fire_alt            = attrs->GetString("inaccuracy fire alt", inaccuracy_fire_alt.c_str());
    inaccuracy_move_alt            = attrs->GetString("inaccuracy move alt", inaccuracy_move_alt.c_str());
    recoil_angle                   = attrs->GetString("recoil angle", recoil_angle.c_str());
    recoil_angle_variance          = attrs->GetString("recoil angle variance", recoil_angle_variance.c_str());
    recoil_magnitude               = attrs->GetString("recoil magnitude", recoil_magnitude.c_str());
    recoil_magnitude_variance      = attrs->GetString("recoil magnitude variance", recoil_magnitude_variance.c_str());
    recoil_angle_alt               = attrs->GetString("recoil angle alt", recoil_angle_alt.c_str());
    recoil_angle_variance_alt      = attrs->GetString("recoil angle variance alt", recoil_angle_variance_alt.c_str());
    recoil_magnitude_alt           = attrs->GetString("recoil magnitude alt", recoil_magnitude_alt.c_str());
    recoil_magnitude_variance_alt  = attrs->GetString("recoil magnitude variance alt", recoil_magnitude_variance_alt.c_str());
    recoil_seed                    = attrs->GetInt("recoil seed", recoil_seed);
    recovery_time_crouch           = attrs->GetString("recovery time crouch", recovery_time_crouch.c_str());
    recovery_time_stand            = attrs->GetString("recovery time stand", recovery_time_stand.c_str());
    full_auto                      = attrs->GetInt("is full auto", full_auto);
    clip_size                      = attrs->GetInt("primary clip size", clip_size);
    armor_ratio                    = attrs->GetString("armor ratio", armor_ratio.c_str());
    model_world                    = attrs->GetString("model_world", model_world.c_str());
    model_player                   = attrs->GetString("model_player", model_player.c_str());
}

max_stars_repo_name: twowordbird/csgo-weapon-json

max_issues_repo_name: twowordbird/csgo-weapon-json

max_forks_repo_name: twowordbird/csgo-weapon-json


text:
/*******************************************************************************
Copyright(c) 2015-2021 Parker Hannifin Corp. All rights reserved.

MIT Licensed. See the included LICENSE.txt for a copy of the full MIT License.
*******************************************************************************/
#include "mscl/TimeSpan.h"

#include <boost/test/unit_test.hpp>

using namespace mscl;

BOOST_AUTO_TEST_SUITE(TimeSpan_Test)

BOOST_AUTO_TEST_CASE(TimeSpan_NanoSeconds)
{
    //create a TimeSpan of 1 hour
    TimeSpan ts = TimeSpan::NanoSeconds(3600000000000);

    //check that calling all the time functions gets 1 hour in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 3600000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 3600000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 3600);
}

BOOST_AUTO_TEST_CASE(TimeSpan_MicroSeconds)
{
    //create a TimeSpan of 1 hour
    TimeSpan ts = TimeSpan::MicroSeconds(static_cast<uint64>(3600000000));

    //check that calling all the time functions gets 1 hour in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 3600000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 3600000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 3600);
}

BOOST_AUTO_TEST_CASE(TimeSpan_MilliSeconds)
{
    //create a TimeSpan of 1 hour
    TimeSpan ts = TimeSpan::MilliSeconds(3600000);

    //check that calling all the time functions gets 1 hour in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 3600000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 3600000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 3600);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Seconds)
{
    //create a TimeSpan of 1 hour
    TimeSpan ts = TimeSpan::Seconds(3600);

    //check that calling all the time functions gets 1 hour in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 3600000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 3600000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 3600);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Minutes)
{
    //create a TimeSpan of 1 hour
    TimeSpan ts = TimeSpan::Minutes(60);

    //check that calling all the time functions gets 1 hour in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 3600000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 3600000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 3600);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Hours)
{
    //create a TimeSpan of 1 hour
    TimeSpan ts = TimeSpan::Hours(1);

    //check that calling all the time functions gets 1 hour in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 3600000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 3600000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 3600);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Days)
{
    //create a TimeSpan of 2 days
    TimeSpan ts = TimeSpan::Days(2);

    //check that calling all the time functions gets 2 days in each unit
    BOOST_CHECK_EQUAL(ts.getNanoseconds(), 172800000000000);
    BOOST_CHECK_EQUAL(ts.getMilliseconds(), 172800000);
    BOOST_CHECK_EQUAL(ts.getSeconds(), 172800);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Operators)
{
    //create TimeSpans to compare
    TimeSpan ts1 = TimeSpan::Days(1);
    TimeSpan ts2 = TimeSpan::Seconds(60);
    TimeSpan ts3 = TimeSpan::Minutes(1);

    BOOST_CHECK_EQUAL(ts1 > ts2, true);
    BOOST_CHECK_EQUAL(ts1 >= ts2, true);
    BOOST_CHECK_EQUAL(ts1 == ts2, false);
    BOOST_CHECK_EQUAL(ts1 != ts2, true);
    BOOST_CHECK_EQUAL(ts1 < ts2, false);
    BOOST_CHECK_EQUAL(ts1 <= ts2, false);

    BOOST_CHECK_EQUAL(ts2 > ts3, false);
    BOOST_CHECK_EQUAL(ts2 >= ts3, true);
    BOOST_CHECK_EQUAL(ts2 == ts3, true);
    BOOST_CHECK_EQUAL(ts2 != ts3, false);
    BOOST_CHECK_EQUAL(ts2 < ts3, false);
    BOOST_CHECK_EQUAL(ts2 <= ts3, true);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Multiplication)
{
    TimeSpan span = TimeSpan::NanoSeconds(500);
    TimeSpan rightFactor1 = span * 4;
    TimeSpan leftFactor1 = 4 * span;
    TimeSpan rightFactor2 = span * 1000000000;
    TimeSpan leftFactor2 = 1000000000 * span;

    BOOST_CHECK_EQUAL(rightFactor1.getNanoseconds(), 2000);
    BOOST_CHECK_EQUAL(leftFactor1.getNanoseconds(), 2000);
    BOOST_CHECK_EQUAL(rightFactor2.getSeconds(), 500);
    BOOST_CHECK_EQUAL(leftFactor2.getSeconds(), 500);
}

BOOST_AUTO_TEST_CASE(TimeSpan_Division)
{
    TimeSpan span = TimeSpan::Seconds(1000);
    TimeSpan rightFactor1 = span / 4;
    TimeSpan leftFactor1 = 4 / span;
    TimeSpan rightFactor2 = span / 1000000000;
    TimeSpan leftFactor2 = 1000000000 / span;

    BOOST_CHECK_EQUAL(rightFactor1.getSeconds(), 250);
    BOOST_CHECK_EQUAL(leftFactor1.getSeconds(), 250);
    BOOST_CHECK_EQUAL(rightFactor2.getNanoseconds(), 1000);
    BOOST_CHECK_EQUAL(leftFactor2.getNanoseconds(), 1000);
}

BOOST_AUTO_TEST_SUITE_END()

ext: cpp

max_stars_repo_name: offworld-projects/MSCL

max_stars_repo_licenses: BSL-1.0 OpenSSL MIT

max_issues_repo_name: offworld-projects/MSCL

max_issues_repo_licenses: BSL-1.0 OpenSSL MIT

max_forks_repo_name: offworld-projects/MSCL

max_forks_repo_licenses: BSL-1.0 OpenSSL MIT


text: #include <boost/iostreams/filter/gzip.hpp>

ext: hpp

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
#include "NetworkMessage.h"
#include <boost/endian/conversion.hpp>
#include <stdexcept>
#include <boost/asio.hpp>
#include <boost/log/trivial.hpp>

/// <summary>
/// Write protobuffer message to the network.
/// </summary>
/// <param name="message">Protobuffer message.</param>
/// <param name="socket">Socket to which the message will be written.</param>
void NetworkMessage::WriteToNetwork(const google::protobuf::Message& message,
                                    boost::asio::ip::tcp::socket& socket,
                                    std::function<void()> handler,
                                    std::function<void()> abortHandler)
{
    std::string client = socket.remote_endpoint().address().to_string();
    google::protobuf::Any packedMsg;
    packedMsg.PackFrom(message);
    size_ = packedMsg.ByteSize();
    serializedMessage_ = std::unique_ptr<char[]>(new char[size_]);
    packedMsg.SerializeToArray(serializedMessage_.get(), size_);
    boost::endian::native_to_big_inplace(size_);
    boost::asio::async_write(
        socket, boost::asio::buffer(&size_, sizeof(size_)),
        [this, &socket, size = packedMsg.ByteSize(), handler, client,
         abortHandler](const boost::system::error_code& error, std::size_t bytes) {
            if (!error)
            {
                boost::asio::async_write(socket, boost::asio::buffer(serializedMessage_.get(), size),
                                         [handler, client, abortHandler](const boost::system::error_code& error,
                                                                         std::size_t bytes) {
                                             if (!error)
                                             {
                                                 handler();
                                             }
                                             else
                                             {
                                                 std::string message = error.message();
                                                 BOOST_LOG_TRIVIAL(info) << client << " " << message;
                                                 abortHandler();
                                             }
                                         });
            }
            else
            {
                std::string message = error.message();
                BOOST_LOG_TRIVIAL(info) << client << " " << message;
                abortHandler();
            }
        });
}

/// <summary>
/// Read message from network.
/// </summary>
/// <param name="socket">Socket from which the message will be read.</param>
/// <returns>Any protobuffer message.</param>
void NetworkMessage::ReadFromNetwork(boost::asio::ip::tcp::socket& socket,
                                     std::function<void(google::protobuf::Any)> handler,
                                     std::function<void()> abortHandler)
{
    std::string client = socket.remote_endpoint().address().to_string();
    boost::asio::async_read(
        socket, boost::asio::buffer(lengthBuffer_, 4),
        [this, &socket, handler, client, abortHandler](const boost::system::error_code& error, std::size_t bytes) {
            if (!error)
            {
                int32_t readSize = *(reinterpret_cast<const int32_t*>(lengthBuffer_.data()));
                boost::endian::big_to_native_inplace(readSize);
                serializedMessage_ = std::unique_ptr<char[]>(new char[readSize]);
                boost::asio::async_read(socket, boost::asio::buffer(serializedMessage_.get(), readSize),
                                        [this, readSize, handler, client,
                                         abortHandler](const boost::system::error_code& error, std::size_t bytes) {
                                            if (!error)
                                            {
                                                google::protobuf::Any ret;
                                                if (!ret.ParseFromArray(serializedMessage_.get(), readSize))
                                                {
                                                    throw std::invalid_argument(
                                                        "Failed to parse message from stream");
                                                }
                                                handler(ret);
                                            }
                                            else
                                            {
                                                std::string message = error.message();
                                                BOOST_LOG_TRIVIAL(info) << client << " " << message;
                                                abortHandler();
                                            }
                                        });
            }
            else
            {
                std::string message = error.message();
                BOOST_LOG_TRIVIAL(info) << client << " " << message;
                abortHandler();
            }
        });
}

void NetworkMessage::WriteRaw(boost::asio::ip::tcp::socket& socket,
                              char* dataBuffer,
                              int32_t elementCount,
                              DataType dataType,
                              std::function<void()> handler,
                              std::function<void()> abortHandler)
{
    int32_t elementSize = GetDataTypeSize(dataType);
    int32_t totalSize = elementCount * elementSize;
    std::string client = socket.remote_endpoint().address().to_string();
    boost::asio::async_write(socket, boost::asio::buffer(dataBuffer, totalSize),
                             [client, handler, abortHandler](const boost::system::error_code& error,
                                                             std::size_t bytes) {
                                 if (!error)
                                 {
                                     handler();
                                 }
                                 else
                                 {
                                     std::string message = error.message();
                                     BOOST_LOG_TRIVIAL(info) << client << " " << message;
                                     abortHandler();
                                 }
                             });
}

void NetworkMessage::ReadRaw(boost::asio::ip::tcp::socket& socket,
                             char* dataBuffer,
                             int32_t elementCount,
                             int32_t dataSize,
                             std::function<void(char*, int32_t)> handler,
                             std::function<void()> abortHandler)
{
    int32_t totalSize = dataSize;
    std::string client = socket.remote_endpoint().address().to_string();
    boost::asio::async_read(socket, boost::asio::buffer(dataBuffer, totalSize),
                            [handler, dataBuffer, elementCount, client,
                             abortHandler](const boost::system::error_code& error, std::size_t bytes) {
                                if (!error)
                                {
                                    handler(dataBuffer, elementCount);
                                }
                                else
                                {
                                    std::string message = error.message();
                                    BOOST_LOG_TRIVIAL(info) << client << " " << message;
                                    abortHandler();
                                }
                            });
}

max_stars_repo_name: veselyja/qikkdb-community

max_issues_repo_name: veselyja/qikkdb-community

max_forks_repo_name: qikkDB/qikkdb


text:
//
// Copyright (c) 2018-2019 Christian Mazakas (christian dot mazakas at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/LeonineKing1199/foxy
//

#ifndef FOXY_DETAIL_TIMED_OP_WRAPPER_HPP_
#define FOXY_DETAIL_TIMED_OP_WRAPPER_HPP_

#include <foxy/session.hpp>
#include <foxy/shared_handler_ptr.hpp>
#include <foxy/detail/close_stream.hpp>

#include <boost/callable_traits/args.hpp>
#include <boost/hof/unpack.hpp>

namespace foxy
{
namespace detail
{
template <class Stream, template <class, class...> class Op, class Handler, class Sig>
struct timed_op_wrapper
{
private:
  // our state needs to store the result type of the main operation because of the when_all delaying
  // the execution of the final handler
  //
  struct state
  {
    using args_t = boost::callable_traits::args_t<Sig>;

    args_t                         args;
    ::foxy::basic_session<Stream>& session;
    int                            ops;
    boost::asio::coroutine         coro;
    boost::asio::coroutine         timer_coro;
    bool                           done;

    boost::asio::executor_work_guard<decltype(session.get_executor())> work;

    explicit state(Handler const& handler, ::foxy::basic_session<Stream>& session_)
      : session(session_)
      , ops{0}
      , done{false}
      , work(session.get_executor())
    {
    }
  };

  ::foxy::shared_handler_ptr<state, Handler> p_;

public:
  timed_op_wrapper()                        = delete;
  timed_op_wrapper(timed_op_wrapper const&) = default;
  timed_op_wrapper(timed_op_wrapper&&)      = default;

  template <class DeducedHandler>
  timed_op_wrapper(::foxy::basic_session<Stream>& session, DeducedHandler&& handler)
    : p_(std::forward<DeducedHandler>(handler), session)
  {
  }

  using executor_type = boost::asio::associated_executor_t<
    Handler,
    decltype(std::declval<::foxy::basic_session<Stream>&>().get_executor())>;

  auto
  get_executor() const noexcept -> executor_type
  {
    return boost::asio::get_associated_executor(p_.handler(), p_->session.get_executor());
  }

  using allocator_type = boost::asio::associated_allocator_t<Handler>;

  auto
  get_allocator() const noexcept -> allocator_type
  {
    return boost::asio::get_associated_allocator(p_.handler());
  }

  template <class... Types, class... Args>
  auto
  init(Args&&... args) -> void
  {
    using boost::beast::bind_handler;
    using namespace std::placeholders;

    auto& s = *p_;
    Op<Types..., typename std::decay<decltype(*this)>::type>(s.session, std::forward<Args>(args)...,
                                                             *this)({}, 0, false);

    s.session.timer.expires_after(s.session.opts.timeout);
    s.session.timer.async_wait(bind_handler(*this, on_timer_t{}, _1));
    (*this)(on_completion_t{}, {});

    p_.reset();
  }

  struct on_timer_t
  {
  };
  struct on_completion_t
  {
  };

  auto
  operator()(on_timer_t, boost::system::error_code ec) -> void
  {
    using namespace std::placeholders;

    auto& s = *p_;
    BOOST_ASIO_CORO_REENTER(s.timer_coro)
    {
      // this means the user called `expires_after` _or_ our timer was cancelled
      //
      while (ec == boost::asio::error::operation_aborted) {
        // we know that we were cancelled if `p_->done` is true
        // note that this implies a precondition that no one  else will be calling cancel on the
        // timer
        //
        if (s.done) { break; }

        // otherwise, the user likely updated the timer's expiration so we need to prolong the async
        // operation accordingly
        //
        BOOST_ASIO_CORO_YIELD
        s.session.timer.async_wait(boost::beast::bind_handler(*this, on_timer_t{}, _1));
      }
    }

    if (!s.timer_coro.is_complete()) { return; }

    s.ops++;
    if (ec || s.done) { return (*this)(on_completion_t{}, {}); }

    auto& stream =
      s.session.stream.is_ssl() ? s.session.stream.ssl().next_layer() : s.session.stream.plain();

    close(stream);
    (*this)(on_completion_t{}, {});
  }

  template <class... Args>
  auto
  operator()(Args&&... args) -> void
  {
    p_->ops++;

    p_->args = typename state::args_t(std::forward<Args>(args)...);
    p_->done = true;

    p_->session.timer.cancel();

    return (*this)(on_completion_t{}, {});
  }

  auto
  operator()(on_completion_t, boost::system::error_code ec) -> void
  {
    namespace hof = boost::hof;

    auto& s = *p_;
    BOOST_ASIO_CORO_REENTER(s.coro)
    {
      while (s.ops < 2) { BOOST_ASIO_CORO_YIELD; }
    }

    if (!s.coro.is_complete()) { return; }

    auto args = std::move(s.args);
    auto work = std::move(s.work);

    auto f = [&](auto&&... args) { p_.invoke(std::forward<decltype(args)>(args)...); };

    hof::unpack(f)(std::move(args));
  }
};

} // namespace detail
} // namespace foxy

#endif // FOXY_DETAIL_TIMED_OP_WRAPPER_HPP_

max_stars_repo_name: djarek/foxy

max_issues_repo_name: djarek/foxy

max_forks_repo_name: djarek/foxy


text:
/*
// Licensed to DynamoBI Corporation (DynamoBI) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  DynamoBI licenses this file
// to you 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 "fennel/common/CommonPreamble.h"
#include "fennel/test/PagingTestBase.h"
#include "fennel/common/FileSystem.h"
#include "fennel/device/RandomAccessFileDevice.h"
#include "fennel/synch/Thread.h"
#include "fennel/cache/CachePage.h"
#include "fennel/cache/CacheImpl.h"
#include "fennel/cache/RandomVictimPolicy.h"
#include <boost/test/test_tools.hpp>

#include <functional>

using namespace fennel;

static boost::thread_specific_ptr<std::subtractive_rng> g_pRNG;

void PagingTestBase::threadInit()
{
    ThreadedTestBase::threadInit();
    g_pRNG.reset(new std::subtractive_rng());
}

void PagingTestBase::threadTerminate()
{
    g_pRNG.reset();
    ThreadedTestBase::threadTerminate();
}

uint PagingTestBase::generateRandomNumber(uint iMax)
{
    return (*g_pRNG)(iMax);
}

void PagingTestBase::fillPage(CachePage &page,uint x)
{
    uint *p = reinterpret_cast<uint *>(page.getWritableData());
    assert(cbPageUsable);
    uint n = cbPageUsable / sizeof(uint);
    for (uint i = 0; i < n; i++) {
        p[i] = x + i;
    }
    uint r = generateRandomNumber(n);
    p[r] = 0;
}

void PagingTestBase::verifyPage(CachePage &page,uint x)
{
    uint const *p = reinterpret_cast<uint const *>(page.getReadableData());
    assert(cbPageUsable);
    uint n = cbPageUsable / sizeof(uint);
    uint nZeros = 0;
    for (uint i = 0; i < n; i++) {
        if (p[i] != x + i) {
            assert(!p[i]);
            nZeros++;
        }
    }
    assert(nZeros < 2);
}

bool PagingTestBase::testOp(OpType opType, uint iPage, bool bNice)
{
    CachePage *pPage = lockPage(opType,iPage);
    LockMode lockMode = getLockMode(opType);
    if (!pPage) {
        // must be NoWait locking failed
        assert(lockMode >= LOCKMODE_S_NOWAIT);
        return false;
    }
    CachePage &page = *pPage;
    if (lockMode == LOCKMODE_S
        || lockMode == LOCKMODE_S_NOWAIT)
    {
        verifyPage(page, iPage);
    } else {
        fillPage(page, iPage);
    }
    switch (lockMode) {
    case LOCKMODE_X_NOWAIT:
        lockMode = LOCKMODE_X;
        break;
    case LOCKMODE_S_NOWAIT:
        lockMode = LOCKMODE_S;
        break;
    default:
        break;
    }
    if (bNice) {
        getCache().nicePage(page);
    }
    unlockPage(page, lockMode);
    return true;
}

char const *PagingTestBase::getOpName(OpType opType)
{
    switch (opType) {
    case OP_ALLOCATE:
        return "allocate";
    case OP_READ_SEQ:
        return "sequential read";
    case OP_WRITE_SEQ:
        return "sequential write";
    case OP_READ_RAND:
        return "random read";
    case OP_WRITE_RAND:
        return "random write";
    case OP_READ_NOWAIT:
        return "read no-wait";
    case OP_WRITE_NOWAIT:
        return "write no-wait";
    case OP_WRITE_SKIP:
        return "write every n pages";
    default:
        permAssert(false);
    }
}

LockMode PagingTestBase::getLockMode(OpType opType)
{
    switch (opType) {
    case OP_ALLOCATE:
        return LOCKMODE_X;
    case OP_READ_SEQ:
        return LOCKMODE_S;
    case OP_WRITE_SEQ:
        return LOCKMODE_X;
    case OP_READ_RAND:
        return LOCKMODE_S;
    case OP_WRITE_RAND:
        return LOCKMODE_X;
    case OP_READ_NOWAIT:
        return LOCKMODE_S_NOWAIT;
    case OP_WRITE_NOWAIT:
        return LOCKMODE_X_NOWAIT;
    case OP_WRITE_SKIP:
        return LOCKMODE_X;
    default:
        permAssert(false);
    }
}

void PagingTestBase::testSequentialOp(OpType opType)
{
    uint n = 0;
    for (uint i = 0; i < nDiskPages; i++) {
        if (testOp(opType, i, true)) {
            n++;
        }
    }
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE(
        "completed " << n << " " << getOpName(opType) << " ops");
}

void PagingTestBase::testRandomOp(OpType opType)
{
    uint n = 0;
    for (uint i = 0; i < nRandomOps; i++) {
        uint iPage = generateRandomNumber(nDiskPages);
        bool bNice = (generateRandomNumber(nRandomOps) == 0);
        if (testOp(opType, iPage, bNice)) {
            n++;
        }
    }
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE(
        "completed " << n << " " << getOpName(opType) << " ops");

}

void PagingTestBase::testSkipOp(OpType opType, uint n)
{
    uint numOps = 0;
    for (uint i = 0; i < nDiskPages; i += n) {
        if (testOp(opType, i, true)) {
            numOps++;
        }
    }
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE(
        "completed " << numOps << " " << getOpName(opType) << " ops");
}

void PagingTestBase::testScratch()
{
    for (uint i = 0; i < nRandomOps; i++) {
        CachePage &page = getCache().lockScratchPage();
        fillPage(page, generateRandomNumber(10000));
        getCache().unlockPage(page, LOCKMODE_X);
    }
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE("completed " << nRandomOps << " random scratch ops");
}

void PagingTestBase::testPrefetch()
{
    // TODO: parameterize this
    uint n = 3;
    for (uint i = 0; i < n; i++) {
        uint iPage = generateRandomNumber(nDiskPages);
        prefetchPage(iPage);
    }
    // give the prefetches a chance to complete
    snooze(1);
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE("completed " << n << " random prefetch ops");
}

void PagingTestBase::testPrefetchBatch()
{
    // TODO: parameterize this
    uint n = 2;
    uint nPagesPerBatch = 4;
    for (uint i = 0; i < n; i++) {
        uint iPage = generateRandomNumber(nDiskPages - nPagesPerBatch);
        prefetchBatch(iPage, nPagesPerBatch);
    }
    // give the prefetches a chance to complete
    snooze(1);
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE("completed " << n << " random prefetch batch ops");
}

void PagingTestBase::testAllocateAll()
{
    testSequentialOp(OP_ALLOCATE);
}

void PagingTestBase::testSequentialRead()
{
    testSequentialOp(OP_READ_SEQ);
}

void PagingTestBase::testSequentialWrite()
{
    testSequentialOp(OP_WRITE_SEQ);
}

void PagingTestBase::testRandomRead()
{
    testRandomOp(OP_READ_RAND);
}

void PagingTestBase::testRandomWrite()
{
    testRandomOp(OP_WRITE_RAND);
}

void PagingTestBase::testSkipWrite(uint n)
{
    testSkipOp(OP_WRITE_SKIP, n);
}

void PagingTestBase::testAllocate()
{
    permAssert(false);
}

void PagingTestBase::testDeallocate()
{
    permAssert(false);
}

void PagingTestBase::testCheckpoint()
{
    DeviceIdPagePredicate pagePredicate(dataDeviceId);
    getCache().checkpointPages(pagePredicate, CHECKPOINT_FLUSH_ALL);
}

void PagingTestBase::testCheckpointGuarded()
{
    snooze(nSecondsBetweenCheckpoints);
    StrictMutexGuard logGuard(logMutex);
    BOOST_MESSAGE("checkpoint started");
    logGuard.unlock();
    SXMutexExclusiveGuard checkpointExclusiveGuard(checkpointMutex);
    testCheckpoint();
    checkpointExclusiveGuard.unlock();
    logGuard.lock();
    BOOST_MESSAGE("checkpoint completed");
}

void PagingTestBase::testCacheResize()
{
    snooze(nSeconds / 3);
    getCache().setAllocatedPageCount(nMemPages / 2);
    StrictMutexGuard mutexGuard(logMutex);
    BOOST_MESSAGE("shrank cache");
    mutexGuard.unlock();
    snooze(nSeconds / 3);
    getCache().setAllocatedPageCount(nMemPages - 1);
    mutexGuard.lock();
    BOOST_MESSAGE("expanded cache");
    mutexGuard.unlock();
}

PagingTestBase::PagingTestBase()
{
    nRandomOps = configMap.getIntParam("randomOps", 5000);
    nSecondsBetweenCheckpoints =
        configMap.getIntParam("checkpointInterval", 20);
    bTestResize = configMap.getIntParam("resizeCache", 1);
    checkpointMutex.setSchedulingPolicy(SXMutex::SCHEDULE_FAVOR_EXCLUSIVE);

    threadCounts.resize(OP_MAX,-1);

    threadCounts[OP_READ_SEQ] = configMap.getIntParam(
        "readSeqThreads",-1);
    threadCounts[OP_WRITE_SEQ] = configMap.getIntParam(
        "writeSeqThreads",-1);
    threadCounts[OP_READ_RAND] = configMap.getIntParam(
        "readRandThreads",-1);
    threadCounts[OP_WRITE_RAND] = configMap.getIntParam(
        "writeRandThreads",-1);
    threadCounts[OP_READ_NOWAIT] = configMap.getIntParam(
        "readNoWaitThreads",-1);
    threadCounts[OP_WRITE_NOWAIT] = configMap.getIntParam(
        "writeNoWaitThreads",-1);
    threadCounts[OP_WRITE_SKIP] = configMap.getIntParam(
        "writeSkipThreads",-1);
    threadCounts[OP_SCRATCH] = configMap.getIntParam(
        "scratchThreads",-1);
    threadCounts[OP_PREFETCH] = configMap.getIntParam(
        "prefetchThreads",-1);
    threadCounts[OP_PREFETCH_BATCH] = configMap.getIntParam(
        "prefetchBatchThreads",-1);
    threadCounts[OP_ALLOCATE] = configMap.getIntParam(
        "allocateThreads",-1);
    threadCounts[OP_DEALLOCATE] = configMap.getIntParam(
        "deallocateThreads",-1);

    if (nSecondsBetweenCheckpoints < nSeconds) {
        threadCounts[OP_CHECKPOINT] = 1;
    } else {
        threadCounts[OP_CHECKPOINT] = 0;
    }

    if (bTestResize) {
        threadCounts[OP_RESIZE_CACHE] = 1;
    } else {
        threadCounts[OP_RESIZE_CACHE] = 0;
    }

    cbPageUsable = 0;

    threadInit();
}

PagingTestBase::~PagingTestBase()
{
    threadTerminate();
}

bool PagingTestBase::testThreadedOp(int iOp)
{
    SXMutexSharedGuard checkpointSharedGuard(checkpointMutex, false);
    assert(iOp < OP_MAX);
    OpType op = static_cast<OpType>(iOp);
    switch (op) {
    case PagingTestBase::OP_WRITE_SEQ:
        checkpointSharedGuard.lock();
        // fall through
    case PagingTestBase::OP_READ_SEQ:
        testSequentialOp(op);
        break;
    case PagingTestBase::OP_WRITE_RAND:
    case PagingTestBase::OP_WRITE_NOWAIT:
        checkpointSharedGuard.lock();
        // fall through
    case PagingTestBase::OP_READ_RAND:
    case PagingTestBase::OP_READ_NOWAIT:
        testRandomOp(op);
        break;
    case PagingTestBase::OP_WRITE_SKIP:
        checkpointSharedGuard.lock();
        testSkipOp(op, 5);
        break;
    case PagingTestBase::OP_SCRATCH:
        testScratch();
        break;
    case PagingTestBase::OP_PREFETCH:
        testPrefetch();
        break;
    case PagingTestBase::OP_PREFETCH_BATCH:
        testPrefetchBatch();
        break;
    case PagingTestBase::OP_ALLOCATE:
        checkpointSharedGuard.lock();
        testAllocate();
        break;
    case PagingTestBase::OP_DEALLOCATE:
        checkpointSharedGuard.lock();
        testDeallocate();
        break;
    case PagingTestBase::OP_CHECKPOINT:
        testCheckpointGuarded();
        break;
    case PagingTestBase::OP_RESIZE_CACHE:
        testCacheResize();
        return false;
    default:
        permAssert(false);
    }
    return true;
}

void PagingTestBase::testMultipleThreads()
{
    openStorage(DeviceMode::createNew);
    testAllocateAll();
    runThreadedTestCase();
}

// End PagingTestBase.cpp


text:
/*
 * Copyright (c) 2021 The Foundation for Research on Information Technologies in Society (IT'IS).
 *
 * This file is part of iSEG
 * (see https://github.com/ITISFoundation/osparc-iseg).
 *
 * This software is released under the MIT License.
 *  https://opensource.org/licenses/MIT
 */
#include "Precompiled.h"

#include "SlicesHandler.h"
#include "TissueHierarchy.h"
#include "TissueInfos.h"

#include "Data/Logger.h"
#include "Data/ScopedTimer.h"

#include "Core/HDF5Reader.h"
#include "Core/HDF5Writer.h"

#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>

#include <vtkSmartPointer.h>

#include <cctype>
#include <cstdio>
#include <fstream>
#include <iostream>

namespace iseg {

namespace {
std::string str_tolower(std::string s)
{
	std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); } // correct
	);
	return s;
}
} // namespace

TissueInfo* TissueInfos::GetTissueInfo(tissues_size_t tissuetype)
{
	return &tissue_infos_vector[tissuetype];
}

const Color& TissueInfos::GetTissueColor(tissues_size_t tissuetype)
{
	return tissue_infos_vector.at(tissuetype).m_Color;
}

std::tuple<std::uint8_t, std::uint8_t, std::uint8_t> iseg::TissueInfos::GetTissueColorMapped(tissues_size_t tissuetype)
{
	if (int(tissuetype) < tissue_infos_vector.size())
	{
		return tissue_infos_vector[tissuetype].m_Color.ToUChar();
	}
	else
	{
		using rgb = std::tuple<std::uint8_t, std::uint8_t, std::uint8_t>;
		if (tissuetype == Mark::red)
		{
			return rgb(255, 0, 0);
		}
		else if (tissuetype == Mark::green)
		{
			return rgb(0, 255, 0);
		}
		else if (tissuetype == Mark::blue)
		{
			return rgb(0, 0, 255);
		}
		else //if (tissuetype == Mark::WHITE)
		{
			return rgb(255, 255, 255);
		}
	}
}

void TissueInfos::GetTissueColorBlendedRGB(tissues_size_t tissuetype, unsigned char& r, unsigned char& g, unsigned char& b, unsigned char offset)
{
	auto& color = tissue_infos_vector.at(tissuetype).m_Color;
	auto opac = tissue_infos_vector.at(tissuetype).m_Opac;
	r = (unsigned char)(offset + opac * (255.0f * color[0] - offset));
	g = (unsigned char)(offset + opac * (255.0f * color[1] - offset));
	b = (unsigned char)(offset + opac * (255.0f * color[2] - offset));
}

float TissueInfos::GetTissueOpac(tissues_size_t tissuetype)
{
	return tissue_infos_vector[tissuetype].m_Opac;
}

std::string TissueInfos::GetTissueName(tissues_size_t tissuetype)
{
	return tissue_infos_vector[tissuetype].m_Name;
}

bool TissueInfos::GetTissueLocked(tissues_size_t tissuetype)
{
	if (tissuetype > tissue_infos_vector.size() - 1)
		return false;

	return tissue_infos_vector[tissuetype].m_Locked;
}

tissues_size_t TissueInfos::GetTissueType(std::string tissuename)
{
	std::string lower = str_tolower(tissuename);
	if (tissue_type_map.find(lower) != tissue_type_map.end())
	{
		return tissue_type_map[lower];
	}
	return 0;
}

void TissueInfos::SetTissueColor(tissues_size_t tissuetype, float r, float g, float b)
{
	tissue_infos_vector[tissuetype].m_Color = Color(r, g, b);
}

void TissueInfos::SetTissueOpac(tissues_size_t tissuetype, float val)
{
	tissue_infos_vector[tissuetype].m_Opac = val;
}

void TissueInfos::SetTissueName(tissues_size_t tissuetype, std::string val)
{
	tissue_type_map.erase(str_tolower(tissue_infos_vector[tissuetype].m_Name));
	tissue_type_map.insert(TissueTypeMapEntryType(str_tolower(val), tissuetype));
	tissue_infos_vector[tissuetype].m_Name = val;
}

void TissueInfos::SetTissueLocked(tissues_size_t tissuetype, bool val)
{
	tissue_infos_vector[tissuetype].m_Locked = val;
}

void TissueInfos::SetTissuesLocked(bool val)
{
	for (auto vec_it = tissue_infos_vector.begin() + 1; vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		vec_it->m_Locked = val;
	}
}

void TissueInfos::InitTissues()
{
	tissue_infos_vector.clear();
	tissue_infos_vector.resize(83);

	tissue_infos_vector[1].m_Name = "Adrenal_gland";
	tissue_infos_vector[1].m_Color = Color(0.338000f, 0.961000f, 0.725000f);
	tissue_infos_vector[2].m_Name = "Air_internal";
	tissue_infos_vector[2].m_Color = Color(0.000000f, 0.000000f, 0.000000f);
	tissue_infos_vector[3].m_Name = "Artery";
	tissue_infos_vector[3].m_Color = Color(0.800000f, 0.000000f, 0.000000f);
	tissue_infos_vector[4].m_Name = "Bladder";
	tissue_infos_vector[4].m_Color = Color(0.529400f, 0.854900f, 0.011800f);
	tissue_infos_vector[5].m_Name = "Blood_vessel";
	tissue_infos_vector[5].m_Color = Color(0.666700f, 0.003900f, 0.003900f);
	tissue_infos_vector[6].m_Name = "Bone";
	tissue_infos_vector[6].m_Color = Color(0.929412f, 0.839216f, 0.584314f);
	tissue_infos_vector[7].m_Name = "Brain_grey_matter";
	tissue_infos_vector[7].m_Color = Color(0.500000f, 0.500000f, 0.500000f);
	tissue_infos_vector[8].m_Name = "Brain_white_matter";
	tissue_infos_vector[8].m_Color = Color(0.900000f, 0.900000f, 0.900000f);
	tissue_infos_vector[9].m_Name = "Breast";
	tissue_infos_vector[9].m_Color = Color(0.996000f, 0.741000f, 1.000000f);
	tissue_infos_vector[10].m_Name = "Bronchi";
	tissue_infos_vector[10].m_Color = Color(0.528000f, 0.592000f, 1.000000f);
	tissue_infos_vector[11].m_Name = "Bronchi_lumen";
	tissue_infos_vector[11].m_Color = Color(0.368600f, 0.474500f, 0.635300f);
	tissue_infos_vector[12].m_Name = "Cartilage";
	tissue_infos_vector[12].m_Color = Color(0.627000f, 0.988000f, 0.969000f);
	tissue_infos_vector[13].m_Name = "Cerebellum";
	tissue_infos_vector[13].m_Color = Color(0.648000f, 0.599000f, 0.838000f);
	tissue_infos_vector[14].m_Name = "Cerebrospinal_fluid";
	tissue_infos_vector[14].m_Color = Color(0.474500f, 0.521600f, 0.854900f);
	tissue_infos_vector[15].m_Name = "Connective_tissue";
	tissue_infos_vector[15].m_Color = Color(1.000000f, 0.705882f, 0.000000f);
	tissue_infos_vector[16].m_Name = "Diaphragm";
	tissue_infos_vector[16].m_Color = Color(0.745000f, 0.188000f, 0.286000f);
	tissue_infos_vector[17].m_Name = "Ear_cartilage";
	tissue_infos_vector[17].m_Color = Color(0.627000f, 0.988000f, 0.969000f);
	tissue_infos_vector[18].m_Name = "Ear_skin";
	tissue_infos_vector[18].m_Color = Color(0.423500f, 0.611800f, 0.603900f);
	tissue_infos_vector[19].m_Name = "Epididymis";
	tissue_infos_vector[19].m_Color = Color(0.000000f, 0.359000f, 1.000000f);
	tissue_infos_vector[20].m_Name = "Esophagus";
	tissue_infos_vector[20].m_Color = Color(1.000000f, 0.585000f, 0.000000f);
	tissue_infos_vector[21].m_Name = "Esophagus_lumen";
	tissue_infos_vector[21].m_Color = Color(1.000000f, 0.789000f, 0.635000f);
	tissue_infos_vector[22].m_Name = "Eye_lens";
	tissue_infos_vector[22].m_Color = Color(0.007800f, 0.658800f, 0.996100f);
	tissue_infos_vector[23].m_Name = "Eye_vitreous_humor";
	tissue_infos_vector[23].m_Color = Color(0.331000f, 0.746000f, 0.937000f);
	tissue_infos_vector[24].m_Name = "Fat";
	tissue_infos_vector[24].m_Color = Color(0.984314f, 0.980392f, 0.215686f);
	tissue_infos_vector[25].m_Name = "Gallbladder";
	tissue_infos_vector[25].m_Color = Color(0.258800f, 0.972500f, 0.274500f);
	tissue_infos_vector[26].m_Name = "Heart_lumen";
	tissue_infos_vector[26].m_Color = Color(1.000000f, 0.000000f, 0.000000f);
	tissue_infos_vector[27].m_Name = "Heart_muscle";
	tissue_infos_vector[27].m_Color = Color(1.000000f, 0.000000f, 0.239000f);
	tissue_infos_vector[28].m_Name = "Hippocampus";
	tissue_infos_vector[28].m_Color = Color(0.915000f, 0.188000f, 1.000000f);
	tissue_infos_vector[29].m_Name = "Hypophysis";
	tissue_infos_vector[29].m_Color = Color(1.000000f, 0.000000f, 0.796000f);
	tissue_infos_vector[30].m_Name = "Hypothalamus";
	tissue_infos_vector[30].m_Color = Color(0.563000f, 0.239000f, 0.754000f);
	tissue_infos_vector[31].m_Name = "Intervertebral_disc";
	tissue_infos_vector[31].m_Color = Color(0.627500f, 0.988200f, 0.968600f);
	tissue_infos_vector[32].m_Name = "Kidney_cortex";
	tissue_infos_vector[32].m_Color = Color(0.000000f, 0.754000f, 0.200000f);
	tissue_infos_vector[33].m_Name = "Kidney_medulla";
	tissue_infos_vector[33].m_Color = Color(0.507000f, 1.000000f, 0.479000f);
	tissue_infos_vector[34].m_Name = "Large_intestine";
	tissue_infos_vector[34].m_Color = Color(1.000000f, 0.303000f, 0.176000f);
	tissue_infos_vector[35].m_Name = "Large_intestine_lumen";
	tissue_infos_vector[35].m_Color = Color(0.817000f, 0.556000f, 0.570000f);
	tissue_infos_vector[36].m_Name = "Larynx";
	tissue_infos_vector[36].m_Color = Color(0.937000f, 0.561000f, 0.950000f);
	tissue_infos_vector[37].m_Name = "Liver";
	tissue_infos_vector[37].m_Color = Color(0.478400f, 0.262700f, 0.141200f);
	tissue_infos_vector[38].m_Name = "Lung";
	tissue_infos_vector[38].m_Color = Color(0.225000f, 0.676000f, 1.000000f);
	tissue_infos_vector[39].m_Name = "Mandible";
	tissue_infos_vector[39].m_Color = Color(0.929412f, 0.839216f, 0.584314f);
	tissue_infos_vector[40].m_Name = "Marrow_red";
	tissue_infos_vector[40].m_Color = Color(0.937300f, 0.639200f, 0.498000f);
	tissue_infos_vector[41].m_Name = "Marrow_white";
	tissue_infos_vector[41].m_Color = Color(0.921600f, 0.788200f, 0.486300f);
	tissue_infos_vector[42].m_Name = "Meniscus";
	tissue_infos_vector[42].m_Color = Color(0.577000f, 0.338000f, 0.754000f);
	tissue_infos_vector[43].m_Name = "Midbrain";
	tissue_infos_vector[43].m_Color = Color(0.490200f, 0.682400f, 0.509800f);
	tissue_infos_vector[44].m_Name = "Muscle";
	tissue_infos_vector[44].m_Color = Color(0.745098f, 0.188235f, 0.286275f);
	tissue_infos_vector[45].m_Name = "Nail";
	tissue_infos_vector[45].m_Color = Color(0.873000f, 0.887000f, 0.880000f);
	tissue_infos_vector[46].m_Name = "Mucosa";
	tissue_infos_vector[46].m_Color = Color(1.000000f, 0.631373f, 0.745098f);
	tissue_infos_vector[47].m_Name = "Nerve";
	tissue_infos_vector[47].m_Color = Color(0.000000f, 0.754000f, 0.479000f);
	tissue_infos_vector[48].m_Name = "Ovary";
	tissue_infos_vector[48].m_Color = Color(0.718000f, 0.000000f, 1.000000f);
	tissue_infos_vector[49].m_Name = "Pancreas";
	tissue_infos_vector[49].m_Color = Color(0.506000f, 0.259000f, 0.808000f);
	tissue_infos_vector[50].m_Name = "Patella";
	tissue_infos_vector[50].m_Color = Color(0.929412f, 0.839216f, 0.584314f);
	tissue_infos_vector[51].m_Name = "Penis";
	tissue_infos_vector[51].m_Color = Color(0.000000f, 0.000000f, 1.000000f);
	tissue_infos_vector[52].m_Name = "Pharynx";
	tissue_infos_vector[52].m_Color = Color(0.368600f, 0.474500f, 0.635300f);
	tissue_infos_vector[53].m_Name = "Prostate";
	tissue_infos_vector[53].m_Color = Color(0.190000f, 0.190000f, 1.000000f);
	tissue_infos_vector[54].m_Name = "Scrotum";
	tissue_infos_vector[54].m_Color = Color(0.366000f, 0.549000f, 1.000000f);
	tissue_infos_vector[55].m_Name = "Skin";
	tissue_infos_vector[55].m_Color = Color(0.746000f, 0.613000f, 0.472000f);
	tissue_infos_vector[56].m_Name = "Skull";
	tissue_infos_vector[56].m_Color = Color(0.929412f, 0.839216f, 0.584314f);
	tissue_infos_vector[57].m_Name = "Small_intestine";
	tissue_infos_vector[57].m_Color = Color(1.000000f, 0.775000f, 0.690000f);
	tissue_infos_vector[58].m_Name = "Small_intestine_lumen";
	tissue_infos_vector[58].m_Color = Color(1.000000f, 0.474500f, 0.635300f);
	tissue_infos_vector[59].m_Name = "Spinal_cord";
	tissue_infos_vector[59].m_Color = Color(0.000000f, 0.732000f, 0.662000f);
	tissue_infos_vector[60].m_Name = "Spleen";
	tissue_infos_vector[60].m_Color = Color(0.682400f, 0.964700f, 0.788200f);
	tissue_infos_vector[61].m_Name = "Stomach";
	tissue_infos_vector[61].m_Color = Color(1.000000f, 0.500000f, 0.000000f);
	tissue_infos_vector[62].m_Name = "Stomach_lumen";
	tissue_infos_vector[62].m_Color = Color(1.000000f, 0.738000f, 0.503000f);
	tissue_infos_vector[63].m_Name = "SAT";
	tissue_infos_vector[63].m_Color = Color(1.000000f, 0.796079f, 0.341176f);
	tissue_infos_vector[64].m_Name = "Teeth";
	tissue_infos_vector[64].m_Color = Color(0.976471f, 0.960784f, 0.905882f);
	tissue_infos_vector[65].m_Name = "Tendon_Ligament";
	tissue_infos_vector[65].m_Color = Color(0.945098f, 0.960784f, 0.972549f);
	tissue_infos_vector[66].m_Name = "Testis";
	tissue_infos_vector[66].m_Color = Color(0.000000f, 0.606000f, 1.000000f);
	tissue_infos_vector[67].m_Name = "Thalamus";
	tissue_infos_vector[67].m_Color = Color(0.000000f, 0.415000f, 0.549000f);
	tissue_infos_vector[68].m_Name = "Thymus";
	tissue_infos_vector[68].m_Color = Color(0.439200f, 0.733300f, 0.549000f);
	tissue_infos_vector[69].m_Name = "Thyroid_gland";
	tissue_infos_vector[69].m_Color = Color(0.321600f, 0.023500f, 0.298000f);
	tissue_infos_vector[70].m_Name = "Tongue";
	tissue_infos_vector[70].m_Color = Color(0.800000f, 0.400000f, 0.400000f);
	tissue_infos_vector[71].m_Name = "Trachea";
	tissue_infos_vector[71].m_Color = Color(0.183000f, 1.000000f, 1.000000f);
	tissue_infos_vector[72].m_Name = "Trachea_lumen";
	tissue_infos_vector[72].m_Color = Color(0.613000f, 1.000000f, 1.000000f);
	tissue_infos_vector[73].m_Name = "Ureter_Urethra";
	tissue_infos_vector[73].m_Color = Color(0.376500f, 0.607800f, 0.007800f);
	tissue_infos_vector[74].m_Name = "Uterus";
	tissue_infos_vector[74].m_Color = Color(0.894000f, 0.529000f, 1.000000f);
	tissue_infos_vector[75].m_Name = "Vagina";
	tissue_infos_vector[75].m_Color = Color(0.608000f, 0.529000f, 1.000000f);
	tissue_infos_vector[76].m_Name = "Vein";
	tissue_infos_vector[76].m_Color = Color(0.000000f, 0.329000f, 1.000000f);
	tissue_infos_vector[77].m_Name = "Vertebrae";
	tissue_infos_vector[77].m_Color = Color(0.929412f, 0.839216f, 0.584314f);
	tissue_infos_vector[78].m_Name = "Pinealbody";
	tissue_infos_vector[78].m_Color = Color(1.000000f, 0.000000f, 0.000000f);
	tissue_infos_vector[79].m_Name = "Pons";
	tissue_infos_vector[79].m_Color = Color(0.000000f, 0.710000f, 0.700000f);
	tissue_infos_vector[80].m_Name = "Medulla_oblongata";
	tissue_infos_vector[80].m_Color = Color(0.370000f, 0.670000f, 0.920000f);
	tissue_infos_vector[81].m_Name = "Cornea";
	tissue_infos_vector[81].m_Color = Color(0.686275f, 0.000000f, 1.000000f);
	tissue_infos_vector[82].m_Name = "Eye_Sclera";
	tissue_infos_vector[82].m_Color = Color(1.000000f, 0.000000f, 0.780392f);

	CreateTissueTypeMap();
}

FILE* TissueInfos::SaveTissues(FILE* fp, unsigned short version)
{
	tissues_size_t tissuecount = GetTissueCount();
	fwrite(&tissuecount, 1, sizeof(tissues_size_t), fp);

	if (version >= 5)
	{
		float id = 1.2345f;
		fwrite(&id, 1, sizeof(float), fp);
		fwrite(&version, 1, sizeof(unsigned short), fp);
	}
	fwrite(&(tissue_infos_vector[0].m_Color[0]), 1, sizeof(float), fp);
	fwrite(&(tissue_infos_vector[0].m_Color[1]), 1, sizeof(float), fp);
	fwrite(&(tissue_infos_vector[0].m_Color[2]), 1, sizeof(float), fp);
	if (version >= 5)
	{
		fwrite(&(tissue_infos_vector[0].m_Opac), 1, sizeof(float), fp);
	}

	TissueInfosVecType::iterator vec_it;
	for (vec_it = tissue_infos_vector.begin() + 1;
			 vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		fwrite(&(vec_it->m_Color[0]), 1, sizeof(float), fp);
		fwrite(&(vec_it->m_Color[1]), 1, sizeof(float), fp);
		fwrite(&(vec_it->m_Color[2]), 1, sizeof(float), fp);
		if (version >= 5)
		{
			fwrite(&(vec_it->m_Opac), 1, sizeof(float), fp);
		}
		int size = static_cast<int>(vec_it->m_Name.length());
		fwrite(&size, 1, sizeof(int), fp);
		fwrite(vec_it->m_Name.c_str(), 1, sizeof(char) * size, fp);
	}

	return fp;
}

namespace {
std::vector<TissueHierarchyItem*> GetLeaves(TissueHierarchyItem* hiearchy)
{
	std::vector<TissueHierarchyItem*> leaves;
	for (auto item : *hiearchy->GetChildren())
	{
		if (item->GetIsFolder())
		{
			auto item_leaves = GetLeaves(item);
			leaves.insert(leaves.end(), item_leaves.begin(), item_leaves.end());
		}
		else
		{
			leaves.insert(leaves.end(), item);
		}
	}
	return leaves;
}

std::map<std::string, std::string>
		BuildHiearchyMap(TissueHierarchyItem* hiearchy)
{
	std::map<std::string, std::string> result;

	if (hiearchy)
	{
		auto leaves = GetLeaves(hiearchy);
		for (auto item : leaves)
		{
			std::vector<std::string> parents;
			auto current = item->GetParent();
			while (current && current != hiearchy)
			{
				parents.push_back(current->GetName().toLocal8Bit().constData());
				current = current->GetParent();
			}
			std::string sep = "/";
			std::string path = boost::algorithm::join(parents, sep);
			std::string tissue = item->GetName().toLocal8Bit().constData();
			result.insert(std::make_pair(tissue, path));
		}
	}

	return result;
}
} // namespace

bool TissueInfos::SaveTissuesHDF(const char* filename, TissueHierarchyItem* hiearchy, bool naked, unsigned short version)
{
	ScopedTimer timer("Write Tissue List");

	namespace fs = boost::filesystem;

	int compression = 1;

	fs::path q_file_name(filename);
	std::string basename = (q_file_name.parent_path() / q_file_name.stem()).string();
	std::string suffix = q_file_name.extension().string();

	// save working directory
	auto oldcwd = fs::current_path();

	// enter the xmf file folder so relative names for hdf5 files work
	fs::current_path(q_file_name.parent_path());

	HDF5Writer writer;
	writer.m_Loud = false;
	std::string fname;
	if (naked)
		fname = basename + suffix;
	else
		fname = basename + ".h5";

	if (!writer.Open(fname, "append"))
	{
		ISEG_ERROR("opening " << fname);
	}
	writer.m_Compression = compression;

	auto hiearchy_map = BuildHiearchyMap(hiearchy);

	if (!writer.CreateGroup(std::string("Tissues")))
	{
		ISEG_ERROR_MSG("creating tissues section");
	}

	float rgbo[4];
	int index1[1];
	std::vector<HDF5Writer::size_type> dim1;
	dim1.resize(1);
	dim1[0] = 4;
	std::vector<HDF5Writer::size_type> dim2;
	dim2.resize(1);
	dim2[0] = 1;

	index1[0] = (int)version;
	if (!writer.Write(index1, dim2, std::string("/Tissues/version")))
	{
		ISEG_ERROR_MSG("writing version");
	}

	rgbo[0] = tissue_infos_vector[0].m_Color[0];
	rgbo[1] = tissue_infos_vector[0].m_Color[1];
	rgbo[2] = tissue_infos_vector[0].m_Color[2];
	rgbo[3] = tissue_infos_vector[0].m_Opac;
	if (!writer.Write(rgbo, dim1, std::string("/Tissues/bkg_rgbo")))
	{
		ISEG_ERROR_MSG("writing rgbo");
	}

	int counter = 1;
	for (auto vec_it = tissue_infos_vector.begin() + 1; vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		std::string tissuename1 = vec_it->m_Name;
		boost::replace_all(tissuename1, "\\", "_");
		boost::replace_all(tissuename1, "/", "_");
		std::string tissuename = tissuename1; //BL TODO tissuename1.toLocal8Bit().constData();

		std::string groupname = std::string("/Tissues/") + tissuename;
		writer.CreateGroup(groupname);
		rgbo[0] = vec_it->m_Color[0];
		rgbo[1] = vec_it->m_Color[1];
		rgbo[2] = vec_it->m_Color[2];
		rgbo[3] = vec_it->m_Opac;

		std::string path = hiearchy_map[tissuename];
		if (!writer.WriteAttribute(path, groupname + "/path"))
		{
			ISEG_ERROR_MSG("writing path");
		}
		if (!writer.Write(rgbo, dim1, groupname + "/rgbo"))
		{
			ISEG_ERROR_MSG("writing rgbo");
		}
		index1[0] = counter++;

		if (!writer.Write(index1, dim2, groupname + "/index"))
		{
			ISEG_ERROR_MSG("writing index");
		}
	}

	writer.Close();

	return true;
}

FILE* TissueInfos::SaveTissueLocks(FILE* fp)
{
	TissueInfosVecType::iterator vec_it;
	for (vec_it = tissue_infos_vector.begin() + 1;
			 vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		unsigned char dummy = (unsigned char)vec_it->m_Locked;
		fwrite(&(dummy), 1, sizeof(unsigned char), fp);
	}

	return fp;
}

FILE* TissueInfos::LoadTissueLocks(FILE* fp)
{
	unsigned char dummy;
	tissue_infos_vector[0].m_Locked = false;
	TissueInfosVecType::iterator vec_it;
	for (vec_it = tissue_infos_vector.begin() + 1;
			 vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		fread(&(dummy), sizeof(unsigned char), 1, fp);
		vec_it->m_Locked = (bool)dummy;
	}

	return fp;
}

FILE* TissueInfos::LoadTissues(FILE* fp, int tissuesVersion)
{
	char name[100];
	tissues_size_t tissuecount;
	if (tissuesVersion > 0)
	{
		fread(&tissuecount, sizeof(tissues_size_t), 1, fp);
	}
	else
	{
		unsigned char uchar_buffer;
		fread(&uchar_buffer, sizeof(unsigned char), 1, fp);
		tissuecount = (tissues_size_t)uchar_buffer;
	}

	tissue_infos_vector.clear();
	tissue_infos_vector.resize(tissuecount + 1);

	float id;
	fread(&id, sizeof(float), 1, fp);
	unsigned short opac_version = 0;
	if (id == 1.2345f)
	{
		fread(&opac_version, sizeof(unsigned short), 1, fp);
		fread(&(tissue_infos_vector[0].m_Color[0]), sizeof(float), 1, fp);
	}
	else
	{
		tissue_infos_vector[0].m_Color[0] = id;
		tissue_infos_vector[0].m_Opac = 0.5f;
	}
	fread(&(tissue_infos_vector[0].m_Color[1]), sizeof(float), 1, fp);
	fread(&(tissue_infos_vector[0].m_Color[2]), sizeof(float), 1, fp);
	if (opac_version >= 5)
	{
		fread(&(tissue_infos_vector[0].m_Opac), sizeof(float), 1, fp);
	}

	TissueInfosVecType::iterator vec_it;
	for (vec_it = tissue_infos_vector.begin() + 1;
			 vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		vec_it->m_Locked = false;
		fread(&(vec_it->m_Color[0]), sizeof(float), 1, fp);
		fread(&(vec_it->m_Color[1]), sizeof(float), 1, fp);
		fread(&(vec_it->m_Color[2]), sizeof(float), 1, fp);
		if (opac_version >= 5)
		{
			fread(&(vec_it->m_Opac), sizeof(float), 1, fp);
		}
		else
		{
			vec_it->m_Opac = 0.5f;
		}
		int size;
		fread(&size, sizeof(int), 1, fp);
		fread(name, sizeof(char) * size, 1, fp);
		if (size > 99)
			throw std::runtime_error("ERROR: in tissue file: setting.bin");
		name[size] = '\0';
		std::string s(name);
		vec_it->m_Name = s;
	}

	CreateTissueTypeMap();

	return fp;
}

bool TissueInfos::LoadTissuesHDF(const char* filename, int tissuesVersion)
{
	ScopedTimer timer("Read Tissue List");

	HDF5Reader reader;
	if (!reader.Open(filename))
	{
		ISEG_ERROR("opening " << filename);
		return false;
	}

	bool ok = true;
	std::vector<std::string> tissues = reader.GetGroupInfo("/Tissues");

	tissues_size_t tissuecount = static_cast<tissues_size_t>(tissues.size() - 2);
	tissue_infos_vector.clear();
	if (tissuecount == 0)
	{
		tissuecount = 1;
		tissue_infos_vector.resize(2);
		tissue_infos_vector[1].m_Locked = false;
		tissue_infos_vector[1].m_Color[0] = 1.0f;
		tissue_infos_vector[1].m_Color[1] = 0;
		tissue_infos_vector[1].m_Color[2] = 0;
		tissue_infos_vector[1].m_Opac = 0.5;
		tissue_infos_vector[1].m_Name = std::string("Tissue1");
	}
	else
	{
		tissue_infos_vector.resize(tissuecount + 1);
	}

	std::vector<float> rgbo;
	rgbo.resize(4);
	int index;
	for (std::vector<std::string>::iterator it = tissues.begin();
			 it != tissues.end(); it++)
	{
		if (std::string("version") == std::string(*it)) {}
		else if (std::string("bkg_rgbo") == std::string(*it))
		{
			ok = ok && (reader.Read(rgbo, "/Tissues/bkg_rgbo") != 0);
			tissue_infos_vector[0].m_Color[0] = rgbo[0];
			tissue_infos_vector[0].m_Color[1] = rgbo[1];
			tissue_infos_vector[0].m_Color[2] = rgbo[2];
			tissue_infos_vector[0].m_Opac = rgbo[3];
		}
		else
		{
			ok = ok && (reader.Read(&index, std::string("/Tissues/") + *it + std::string("/index")) != 0);
			ok = ok && (reader.Read(rgbo, std::string("/Tissues/") + *it + std::string("/rgbo")) != 0);
			tissue_infos_vector[index].m_Locked = false;
			tissue_infos_vector[index].m_Color[0] = rgbo[0];
			tissue_infos_vector[index].m_Color[1] = rgbo[1];
			tissue_infos_vector[index].m_Color[2] = rgbo[2];
			tissue_infos_vector[index].m_Opac = rgbo[3];
			tissue_infos_vector[index].m_Name = std::string(*it);
		}
	}

	CreateTissueTypeMap();

	reader.Close();

	return ok;
}

bool TissueInfos::SaveTissuesReadable(const char* filename, unsigned short version)
{
	FILE* fp;
	if ((fp = fopen(filename, "w")) == nullptr)
	{
		return false;
	}
	else
	{
		if (version >= 5)
		{
			fprintf(fp, "V%u\n", (unsigned)version);
		}
		tissues_size_t tissuecount = GetTissueCount();
		fprintf(fp, "N%u\n", tissuecount);

		TissueInfosVecType::iterator vec_it;
		if (version < 5)
		{
			for (vec_it = tissue_infos_vector.begin() + 1;
					 vec_it != tissue_infos_vector.end(); ++vec_it)
			{
				fprintf(fp, "C%f %f %f %s\n", vec_it->m_Color[0], vec_it->m_Color[1], vec_it->m_Color[2], vec_it->m_Name.c_str());
			}
		}
		else
		{
			for (vec_it = tissue_infos_vector.begin() + 1;
					 vec_it != tissue_infos_vector.end(); ++vec_it)
			{
				fprintf(fp, "C%f %f %f %f %s\n", vec_it->m_Color[0], vec_it->m_Color[1], vec_it->m_Color[2], vec_it->m_Opac, vec_it->m_Name.c_str());
			}
		}

		fclose(fp);
		return true;
	}
}

bool TissueInfos::LoadTissuesReadable(const char* filename, SlicesHandler* handler3D, tissues_size_t& removeTissuesRange)
{
	// To replace the old tissue list, handler3D->remove_tissues(removeTissuesRange) has to be called afterwards!

	char name[100];
	FILE* fp;
	if ((fp = fopen(filename, "r")) == nullptr)
	{
		InitTissues();
		return false;
	}
	else
	{
		unsigned short version = 0;
		unsigned int tc;

		// Get version number
		if (fscanf(fp, "V%u\n", &tc) != 1)
		{
			fclose(fp);
			if ((fp = fopen(filename, "r")) == nullptr)
			{
				InitTissues();
				return false;
			}
		}
		else
		{
			version = tc;
		}

		// Get number of labels
		bool is_free_surfer = false;
		int r, g, b, a, res, curr_label;
		if (fscanf(fp, "N%u\n", &tc) != 1)
		{
			fclose(fp);
			if ((fp = fopen(filename, "r")) == nullptr)
			{
				InitTissues();
				return false;
			}

			// Detect FreeSurfer color table format by searching for Unknown label
			curr_label = res = 0;
			r = g = b = a = -1;
			while (res != EOF && res != 6)
			{
				res = fscanf(fp, "%d\t%s\t%d\t%d\t%d\t%d\n", &curr_label, name, &r, &g, &b, &a);
				if (curr_label == r == g == b == a == 0 &&
						std::string("Unknown") == std::string(name))
				{
					is_free_surfer = true;
				}
			}
			if (!is_free_surfer)
			{
				// Unknown format
				fclose(fp);
				InitTissues();
				return false;
			}

			// Get largest label index
			tissues_size_t label_max = 0;
			while ((res = fscanf(fp, "%d\t%s\t%d\t%d\t%d\t%d\n", &curr_label, name, &r, &g, &b, &a)) != EOF)
			{
				if (res == 6)
				{
					label_max = std::max(int(label_max), curr_label);
				}
			}
			tc = label_max;

			fclose(fp);
			if ((fp = fopen(filename, "r")) == nullptr)
			{
				InitTissues();
				return false;
			}
		}

		std::set<tissues_size_t> missing_tissues;
		for (tissues_size_t type = 0; type <= TissueInfos::GetTissueCount();
				 ++type)
		{
			missing_tissues.insert(type);
		}

		tissues_size_t tc1 = (tissues_size_t)tc;
		if (tc > TISSUES_SIZE_MAX)
			tc1 = TISSUES_SIZE_MAX;

		TissueInfosVecType new_tissue_infos_vec;
		if (is_free_surfer)
		{
			// Read first tissue from file
			curr_label = res = 0;
			while (res != EOF && (res != 6 || curr_label == 0))
			{
				res = fscanf(fp, "%d\t%s\t%d\t%d\t%d\t%d\n", &curr_label, name, &r, &g, &b, &a);
			}

			tissues_size_t dummy_idx = 1;
			for (tissues_size_t new_type_idx = 0; new_type_idx < tc1; ++new_type_idx)
			{
				TissueInfo new_tissue_info;
				new_tissue_info.m_Locked = false;
				if (curr_label == new_type_idx + 1)
				{
					new_tissue_info.m_Color[0] = r / 255.0f;
					new_tissue_info.m_Color[1] = g / 255.0f;
					new_tissue_info.m_Color[2] = b / 255.0f;
					new_tissue_info.m_Opac = a / 255.0f;
					new_tissue_info.m_Name = std::string(name);

					// Read next tissue from file
					res = 0;
					while (res != EOF && res != 6)
					{
						res = fscanf(fp, "%d\t%s\t%d\t%d\t%d\t%d\n", &curr_label, name, &r, &g, &b, &a);
					}
				}
				else
				{
					// Dummy tissue
					new_tissue_info.m_Color[0] = float(rand()) / RAND_MAX;
					new_tissue_info.m_Color[1] = float(rand()) / RAND_MAX;
					new_tissue_info.m_Color[2] = float(rand()) / RAND_MAX;
					new_tissue_info.m_Opac = 0.5f;
					new_tissue_info.m_Name = (boost::format("DummyTissue%1") % dummy_idx++).str();
				}
				new_tissue_infos_vec.push_back(new_tissue_info);

				// Check whether the tissue existed before
				tissues_size_t old_type = GetTissueType(new_tissue_info.m_Name);
				if (old_type > 0 &&
						missing_tissues.find(old_type) != missing_tissues.end())
				{
					missing_tissues.erase(missing_tissues.find(old_type));
				}
			}
		}
		else if (version < 5)
		{
			for (tissues_size_t new_type_idx = 0; new_type_idx < tc1; ++new_type_idx)
			{
				TissueInfo new_tissue_info;
				new_tissue_info.m_Locked = false;
				if (fscanf(fp, "C%f %f %f %s\n", &(new_tissue_info.m_Color[0]), &(new_tissue_info.m_Color[1]), &(new_tissue_info.m_Color[2]), name) != 4)
				{
					fclose(fp);
					InitTissues();
					return false;
				}
				new_tissue_info.m_Opac = 0.5f;
				new_tissue_info.m_Name = std::string(name);
				new_tissue_infos_vec.push_back(new_tissue_info);

				// Check whether the tissue existed before
				tissues_size_t old_type = GetTissueType(new_tissue_info.m_Name);
				if (old_type > 0 &&
						missing_tissues.find(old_type) != missing_tissues.end())
				{
					missing_tissues.erase(missing_tissues.find(old_type));
				}
			}
		}
		else
		{
			for (tissues_size_t new_type_idx = 0; new_type_idx < tc1; ++new_type_idx)
			{
				TissueInfo new_tissue_info;
				new_tissue_info.m_Locked = false;
				if (fscanf(fp, "C%f %f %f %f %s\n", &(new_tissue_info.m_Color[0]), &(new_tissue_info.m_Color[1]), &(new_tissue_info.m_Color[2]), &(new_tissue_info.m_Opac), name) != 5)
				{
					fclose(fp);
					InitTissues();
					return false;
				}
				new_tissue_info.m_Name = std::string(name);
				new_tissue_infos_vec.push_back(new_tissue_info);

				// Check whether the tissue existed before
				tissues_size_t old_type = GetTissueType(new_tissue_info.m_Name);
				if (old_type > 0 &&
						missing_tissues.find(old_type) != missing_tissues.end())
				{
					missing_tissues.erase(missing_tissues.find(old_type));
				}
			}
		}

		// Prepend existing tissues (including background) which the input file did not contain
		removeTissuesRange = static_cast<tissues_size_t>(missing_tissues.size() - 1);
		for (std::set<tissues_size_t>::reverse_iterator riter = missing_tissues.rbegin(); riter != missing_tissues.rend(); ++riter)
		{
			new_tissue_infos_vec.insert(new_tissue_infos_vec.begin(), tissue_infos_vector[*riter]);
		}

		// Permute tissue indices according to ordering in input file
		std::vector<tissues_size_t> idx_map(tissue_infos_vector.size());
		for (tissues_size_t old_idx = 0; old_idx < tissue_infos_vector.size(); ++old_idx)
		{
			idx_map[old_idx] = old_idx;
		}
		bool permute = false;
		for (tissues_size_t new_idx = 0; new_idx < new_tissue_infos_vec.size(); ++new_idx)
		{
			tissues_size_t old_idx = GetTissueType(new_tissue_infos_vec[new_idx].m_Name);
			if (old_idx > 0 && old_idx != new_idx)
			{
				idx_map[old_idx] = new_idx;
				permute = true;
			}
		}
		if (permute)
		{
			handler3D->MapTissueIndices(idx_map);
		}

		// Assign new infos vector
		tissue_infos_vector = new_tissue_infos_vec;

		fclose(fp);
		CreateTissueTypeMap();
		return true;
	}
}

bool TissueInfos::SaveDefaultTissueList(const char* filename)
{
	FILE* fp;
	fp = fopen(filename, "w");
	if (fp == nullptr)
	{
		return false;
	}

	TissueInfosVecType::iterator vec_it;
	for (vec_it = tissue_infos_vector.begin() + 1;
			 vec_it != tissue_infos_vector.end(); ++vec_it)
	{
		std::string tissuename1 = vec_it->m_Name;
		boost::replace_all(tissuename1, " ", "_");
		fprintf(fp, "%s %f %f %f %f\n", tissuename1.c_str(), vec_it->m_Color[0], vec_it->m_Color[1], vec_it->m_Color[2], vec_it->m_Opac);
	}
	fclose(fp);
	return true;
}

bool TissueInfos::LoadDefaultTissueList(const char* filename)
{
	FILE* fp;
	fp = fopen(filename, "r");
	if (fp == nullptr)
	{
		InitTissues();
	}
	else
	{
		Color rgb;
		float alpha;
		char name1[1000];

		tissue_infos_vector.clear();
		tissue_infos_vector.resize(1); // Background
		while (fscanf(fp, "%s %f %f %f %f", name1, &rgb.r, &rgb.g, &rgb.b, &alpha) == 5)
		{
			TissueInfo new_tissue_info;
			new_tissue_info.m_Locked = false;
			new_tissue_info.m_Color = rgb;
			new_tissue_info.m_Opac = alpha;
			new_tissue_info.m_Name = name1;
			tissue_infos_vector.push_back(new_tissue_info);
		}
		fclose(fp);
	}

	CreateTissueTypeMap();
	return true;
}

tissues_size_t TissueInfos::GetTissueCount()
{
	return static_cast<tissues_size_t>(tissue_infos_vector.size()) - 1; // Tissue index 0 is the background
}

void TissueInfos::AddTissue(TissueInfo& tissue)
{
	tissue_infos_vector.push_back(tissue);
	tissue_type_map.insert(TissueTypeMapEntryType(str_tolower(tissue.m_Name), GetTissueCount()));
}

void TissueInfos::RemoveTissue(tissues_size_t tissuetype)
{
	// tissueTypeMap.erase(tissueInfosVector[tissuetype].name.toLower());
	tissue_infos_vector.erase(tissue_infos_vector.begin() + tissuetype);
	CreateTissueTypeMap();
}

void TissueInfos::RemoveTissues(const std::set<tissues_size_t>& tissuetypes)
{
	// Remove in descending order
	for (auto riter = tissuetypes.rbegin(); riter != tissuetypes.rend(); ++riter)
	{
		// tissueTypeMap.erase(tissueInfosVector[*riter].name.toLower());
		tissue_infos_vector.erase(tissue_infos_vector.begin() + *riter);
	}
	CreateTissueTypeMap();
}

void TissueInfos::RemoveAllTissues()
{
	tissue_type_map.clear();
	tissue_infos_vector.clear();
	tissue_infos_vector.resize(1); // Background
}

void TissueInfos::CreateTissueTypeMap()
{
	tissue_type_map.clear();
	for (tissues_size_t type = 1; type <= GetTissueCount(); ++type)
	{
		tissue_type_map.insert(TissueTypeMapEntryType(str_tolower(tissue_infos_vector[type].m_Name), type));
	}
}

namespace {
bool is_valid(tissues_size_t i)
{
	return (i <= TissueInfos::GetTissueCount());
}
std::set<tissues_size_t> selection;
} // namespace

std::set<iseg::tissues_size_t> TissueInfos::GetSelectedTissues()
{
	std::set<tissues_size_t> r;
	std::copy_if(selection.begin(), selection.end(), std::inserter(r, r.end()), is_valid);
	return r;
}

void TissueInfos::SetSelectedTissues(const std::set<tissues_size_t>& sel)
{
	if (std::all_of(sel.begin(), sel.end(), is_valid))
	{
		selection = sel;
	}
}

TissueInfos::TissueInfosVecType TissueInfos::tissue_infos_vector;
TissueInfos::TissueTypeMapType TissueInfos::tissue_type_map;

} // namespace iseg

max_stars_repo_name: ITISFoundation/osparc-iseg

max_issues_repo_name: dyollb/osparc-iseg

max_forks_repo_name: dyollb/osparc-iseg


text:
/**
 * @file SparseCholesky.hpp
 * @license License BSD-3-Clause
 * @date 2019-10-06
 * @author Timothy A. Davis
 * @copyright LDL Copyright (c) 2005-2012 by Timothy A. Davis. http://www.suitesparse.com
 *
 * LDL License:
 *
 *    Your use or distribution of LDL or any modified version of
 *    LDL implies that you agree to this License.
 *
 *    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 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 *    USA
 *
 *    Permission is hereby granted to use or copy this program under the
 *    terms of the GNU LGPL, provided that the Copyright, this License,
 *    and the Availability of the original version is retained on all copies.
 *    User documentation of any code that uses this code or any modified
 *    version of this code must cite the Copyright, this License, the
 *    Availability note, and "Used by permission." Permission to modify
 *    the code and to distribute modified code is granted, provided the
 *    Copyright, this License, and the Availability note are retained,
 *    and a notice that the code was modified is included.
 *
 * Availability:
 *
 *    http://www.suitesparse.com
 *
 * Stripped down by Alexander Domahidi, 2012.
 * Modified to c++ code by New York University and Max Planck Gesellschaft, 2017
 *
 */

#pragma once

#include <memory>
#include <Eigen/Sparse>
#include <solver/interface/SolverSetting.hpp>

namespace linalg {

  /**
   * Class to perform an LDL factorization of a matrix
   */
  class SparseCholesky
  {
    public:
	  SparseCholesky(){}
	  ~SparseCholesky(){}

	  void analyzePattern(const Eigen::SparseMatrix<double>& mat, const solver::SolverSetting& stgs);
	  int  factorize(const Eigen::SparseMatrix<double>& mat, const Eigen::Ref<const Eigen::VectorXd>& sign);
	  void solve(const Eigen::Ref<const Eigen::VectorXd>& b, double* x);
	  Eigen::VectorXd& solve(const Eigen::VectorXd& b);

    private:
      inline const solver::SolverSetting& getSetting() const { return *setting_; }

	  int n_;
	  double eps_, delta_;
	  Eigen::VectorXd D_, Y_, X_;
	  Eigen::SparseMatrix<double> L_;
	  Eigen::VectorXi Parent_, Pattern_, Flag_, Lnnz_;
	  std::shared_ptr<const solver::SolverSetting> setting_;

  };

}


text:
/******************************************************************************
**
** Copyright (C) 2009-2012 Kyle Lutz <kyle.r.lutz@gmail.com>
** All rights reserved.
**
** This file is a part of the chemkit project. For more information
** see <http://www.chemkit.org>.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in the
**     documentation and/or other materials provided with the distribution.
**   * Neither the name of the chemkit project nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
******************************************************************************/

#include "moriguchitest.h"

#include <boost/range/algorithm.hpp>

#include <chemkit/molecule.h>
#include <chemkit/moleculardescriptor.h>

void MoriguchiTest::initTestCase()
{
    // verify that the moriguchi-logp plugin registered itself correctly
    QVERIFY(boost::count(chemkit::MolecularDescriptor::descriptors(), "moriguchi-logp") == 1);
}

void MoriguchiTest::logP_data()
{
    QTest::addColumn<QString>("smilesString");
    QTest::addColumn<QString>("formulaString");
    QTest::addColumn<double>("logP");

    // data from examples in [Moriguchi 1992]
    QTest::newRow("halothane") << "C(C(F)(F)F)(Cl)Br" << "C2HBrClF3" << 2.60;
    QTest::newRow("ampicillin") << "O=C(O)[C@@H]2N3C(=O)[C@@H](NC(=O)[C@@H](c1ccccc1)N)[C@H]3SC2(C)C" << "C16H19N3O4S" << 1.06;
    QTest::newRow("valerolactone") << "CC1CCC(=O)O1" << "C5H8O2" << 0.60;
    QTest::newRow("oxazepam") << "C1=CC=C(C=C1)C2=NC(C(=O)NC3=C2C=C(C=C3)Cl)O" << "C15H11ClN2O2" << 3.12;

    // data from Table 2 in [Moriguchi 1994]
    QTest::newRow("atropine") << "CN3[C@H]1CC[C@@H]3C[C@@H](C1)OC(=O)C(CO)c2ccccc2" << "C17H23NO3" << 2.21;
    QTest::newRow("chloramphenicol") << "c1cc(ccc1[C@H]([C@@H](CO)NC(=O)C(Cl)Cl)O)[N+](=O)[O-]" << "C11H12Cl2N2O5" << 1.23;
    //QTest::newRow("chlorothiazide") << "O=S(=O)(c1c(Cl)cc2c(c1)S(=O)(=O)/N=C\\N2)N" << "C7H6ClN3O4S2" << -0.36;
    //QTest::newRow("chlorpromazine") << "CN(C)CCCN1c2ccccc2Sc3c1cc(cc3)Cl" << "C17H19ClN2S" << 3.77;
    QTest::newRow("cimetidine") << "N#CN\\C(=N/C)NCCSCc1ncnc1C" << "C10H16N6S" << 0.82;
    QTest::newRow("diazepam") << "CN1c2ccc(cc2C(=NCC1=O)c3ccccc3)Cl" << "C16H13ClN2O" << 3.36;
    QTest::newRow("diltiazem") << "O=C2N(c3c(S[C@@H](c1ccc(OC)cc1)[C@H]2OC(=O)C)cccc3)CCN(C)C" << "C22H26N2O4S" << 2.67;
    QTest::newRow("diphenhydramine") << "O(CCN(C)C)C(c1ccccc1)c2ccccc2" << "C17H21NO" << 3.26;
    QTest::newRow("disopyramide") << "O=C(N)C(c1ncccc1)(c2ccccc2)CCN(C(C)C)C(C)C" << "C21H29N3O" << 2.47;
    QTest::newRow("flufenamic acid") << "FC(F)(F)c1cc(ccc1)Nc2ccccc2C(=O)O" << "C14H10F3NO2" << 3.86;
    QTest::newRow("haloperidol") << "c1cc(ccc1C(=O)CCCN2CCC(CC2)(c3ccc(cc3)Cl)O)F" << "C21H23ClFNO2" << 4.01;
    QTest::newRow("imipramine") << "c1cc3c(cc1)CCc2c(cccc2)N3CCCN(C)C" << "C19H24N2" << 3.88;
    QTest::newRow("lidocaine") << "O=C(Nc1c(cccc1C)C)CN(CC)CC" << "C14H22N2O" << 2.52;
    QTest::newRow("phenobarbital") << "O=C1NC(=O)NC(=O)C1(c2ccccc2)CC" << "C12H12N2O3" << 0.78;
    QTest::newRow("phenytoin") << "O=C2NC(=O)NC2(c1ccccc1)c3ccccc3" << "C15H12N2O2" << 1.80;
    QTest::newRow("procainamide") << "O=C(c1ccc(N)cc1)NCCN(CC)CC" << "C13H21N3O" << 1.72;
    QTest::newRow("propafenone") << "O=C(c1ccccc1OCC(O)CNCCC)CCc2ccccc2" << "C21H27NO3" << 3.00;
    QTest::newRow("propranolol") << "CC(C)NCC(COc1cccc2c1cccc2)O" << "C16H21NO2" << 2.53;
    QTest::newRow("tetracaine") << "O=C(OCCN(C)C)c1ccc(NCCCC)cc1" << "C15H24N2O2" << 2.64;
    //QTest::newRow("trimethoprim") << "COc1cc(cc(c1OC)OC)Cc2cnc(nc2N)N" << "C14H18N4O3" << 1.26;
    QTest::newRow("verapamil") << "N#CC(c1cc(OC)c(OC)cc1)(CCCN(CCc2ccc(OC)c(OC)c2)C)C(C)C" << "C27H38N2O4" << 3.23;
}

void MoriguchiTest::logP()
{
    QFETCH(QString, smilesString);
    QFETCH(QString, formulaString);
    QFETCH(double, logP);

    QByteArray smiles = smilesString.toAscii();
    QByteArray formula = formulaString.toAscii();

    chemkit::Molecule molecule(smiles.constData(), "smiles");
    QCOMPARE(molecule.formula().c_str(), formula.constData());

    double tolerance = 0.1;
    double actual = molecule.descriptor("moriguchi-logp").toDouble();
    bool equal = std::abs(logP - actual) < tolerance;
    if(!equal){
        qDebug() << "Actual: " << actual;
        qDebug() << "Expected: " << logP;
    }
    QVERIFY(equal);
}

QTEST_APPLESS_MAIN(MoriguchiTest)

max_stars_repo_name: quizzmaster/chemkit

max_issues_repo_name: soplwang/chemkit

max_forks_repo_name: soplwang/chemkit