<commit_before>#include "gtest/gtest.h"
#include "core/angle.h"
#include "core/numeric.h"

#define GTEST(X) TEST(angle, X)

GTEST(constructor_degrees) {
  const auto a = Angle::FromDegrees(180);
  EXPECT_FLOAT_EQ(180, a.inDegrees());
}

GTEST(constructor_degrees_pi) {
  const auto a = Angle::FromDegrees(180);
  EXPECT_FLOAT_EQ(Pi(), a.inRadians());
}

GTEST(zero) {
  const auto a = Angle::Zero();
  EXPECT_FLOAT_EQ(0.0f, a.inDegrees());
}

GTEST(constructor_radians) {
  const auto a = Angle::FromRadians(Pi());
  EXPECT_FLOAT_EQ(180, a.inDegrees());
}

GTEST(constructor_percent) {
  const auto a = Angle::FromPercentOf360(0.5f);
  EXPECT_FLOAT_EQ(180, a.inDegrees());
}

GTEST(wrapped) {
  const auto a = Angle::FromDegrees(360+90).GetWrapped();
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
}

GTEST(wrap) {
  auto a = Angle::FromDegrees(360+90);
  EXPECT_FLOAT_EQ(360.0f + 90.0f, a.inDegrees());
  a.Wrap();
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
}

GTEST(add) {
  auto a = Angle::FromDegrees(90);
  a += Angle::FromRadians(Pi()/2.0f);
  EXPECT_FLOAT_EQ(180.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(180.0f, (Angle::FromDegrees(90) + Angle::FromRadians(Pi()/2.0f)).inDegrees());
}

GTEST(sub) {
  auto a = Angle::FromDegrees(180);
  a -= Angle::FromRadians(Pi()/2.0f);
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(90.0f, (Angle::FromDegrees(180) - Angle::FromRadians(Pi()/2.0f)).inDegrees());
}

GTEST(multi) {
  auto a = Angle::FromDegrees(90);
  a *= 2.0f;
  EXPECT_FLOAT_EQ(180.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(180.0f, (Angle::FromDegrees(90) * 2.0f).inDegrees());
  EXPECT_FLOAT_EQ(180.0f, (2.0f * Angle::FromDegrees(90)).inDegrees());
}

GTEST(div) {
  auto a = Angle::FromDegrees(180);
  a /= 2;
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(90.0f, (Angle::FromDegrees(180) / 2.0f).inDegrees());
}

struct KnownConstantsFromWikipedia : public ::testing::Test {
  // https://en.wikipedia.org/wiki/Trigonometric_functions#Explicit_values
  // https://en.wikipedia.org/wiki/Trigonometric_constants_expressed_in_real_radicals#Table_of_some_common_angles
  const float a = (Sqrt(6.0f) - Sqrt(2.0f)) / 4.0f;
  const float b = (Sqrt(2.0f) + Sqrt(6.0f)) / 4.0f;
};

const float NEAR  = 0.0000001f;
const float NEAR2 = 0.000001f;
const float NEAR3 = 0.00001f;

TEST_F(KnownConstantsFromWikipedia, sin) {
  EXPECT_NEAR(0.0f, Sin(Angle::FromDegrees(0.0f)), NEAR);
  EXPECT_NEAR(1.0f, Sin(Angle::FromDegrees(90.0f)), NEAR);
  EXPECT_NEAR(0.0f, Sin(Angle::FromDegrees(180.0f)), NEAR);
  EXPECT_NEAR(0.5f, Sin(Angle::FromDegrees(30.0f)), NEAR);
  EXPECT_NEAR(a, Sin(Angle::FromDegrees(15.0f)), NEAR);
  EXPECT_NEAR(b, Sin(Angle::FromDegrees(75.0f)), NEAR);
}

TEST_F(KnownConstantsFromWikipedia, cos) {
  EXPECT_NEAR(1.0f, Cos(Angle::FromDegrees(0.0f)), NEAR);
  EXPECT_NEAR(0.0f, Cos(Angle::FromDegrees(90.0f)), NEAR);
  EXPECT_NEAR(-1.0f, Cos(Angle::FromDegrees(180.0f)), NEAR);
  EXPECT_NEAR(0.5f, Cos(Angle::FromDegrees(60.0f)), NEAR);
  EXPECT_NEAR(b, Cos(Angle::FromDegrees(15.0f)), NEAR);
  EXPECT_NEAR(a, Cos(Angle::FromDegrees(75.0f)), NEAR);
}

GTEST(tan) {
  EXPECT_NEAR(0.0f, Tan(Angle::FromDegrees(0.0f)), NEAR);
  EXPECT_NEAR(1.0f, Tan(Angle::FromDegrees(45.0f)), NEAR);
  EXPECT_NEAR(0.0f, Tan(Angle::FromDegrees(180.0f)), NEAR);
  EXPECT_NEAR(1.0f, Tan(Angle::FromDegrees(225.0f)), NEAR2);
  EXPECT_NEAR(Sqrt(3.0f), Tan(Angle::FromDegrees(60.0f)), NEAR2);
}

TEST_F(KnownConstantsFromWikipedia, asin) {
  EXPECT_NEAR(0.0f, Asin(0.0f).inDegrees(), NEAR);
  EXPECT_NEAR(90.0f, Asin(1.0f).inDegrees(), NEAR);
  EXPECT_NEAR(30.0f, Asin(0.5f).inDegrees(), NEAR);
  EXPECT_NEAR(15.0f, Asin(a).inDegrees(), NEAR2);
  EXPECT_NEAR(75.0f, Asin(b).inDegrees(), NEAR3);
}

TEST_F(KnownConstantsFromWikipedia, acos) {
  EXPECT_NEAR(0.0f, Acos(1.0f).inDegrees(), NEAR);
  EXPECT_NEAR(90.0f, Acos(0.0f).inDegrees(), NEAR);
  EXPECT_NEAR(180.0f, Acos(-1.0f).inDegrees(), NEAR);
  EXPECT_NEAR(60.0f, Acos(0.5f).inDegrees(), NEAR);
  EXPECT_NEAR(15.0f, Acos(b).inDegrees(), NEAR3);
  EXPECT_NEAR(75.0f, Acos(a).inDegrees(), NEAR3);
}

GTEST(atan) {
  EXPECT_NEAR(0, Atan(0.0f).inDegrees(), NEAR);
  EXPECT_NEAR(45.0f, Atan(1.0f).inDegrees(), NEAR);
  EXPECT_NEAR(60.0f, Atan(Sqrt(3.0f)).inDegrees(), NEAR2);
}<commit_msg>osx test fixes<commit_after>#include "gtest/gtest.h"
#include "core/angle.h"
#include "core/numeric.h"

#define GTEST(X) TEST(angle, X)

GTEST(constructor_degrees) {
  const auto a = Angle::FromDegrees(180);
  EXPECT_FLOAT_EQ(180, a.inDegrees());
}

GTEST(constructor_degrees_pi) {
  const auto a = Angle::FromDegrees(180);
  EXPECT_FLOAT_EQ(Pi(), a.inRadians());
}

GTEST(zero) {
  const auto a = Angle::Zero();
  EXPECT_FLOAT_EQ(0.0f, a.inDegrees());
}

GTEST(constructor_radians) {
  const auto a = Angle::FromRadians(Pi());
  EXPECT_FLOAT_EQ(180, a.inDegrees());
}

GTEST(constructor_percent) {
  const auto a = Angle::FromPercentOf360(0.5f);
  EXPECT_FLOAT_EQ(180, a.inDegrees());
}

GTEST(wrapped) {
  const auto a = Angle::FromDegrees(360+90).GetWrapped();
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
}

GTEST(wrap) {
  auto a = Angle::FromDegrees(360+90);
  EXPECT_FLOAT_EQ(360.0f + 90.0f, a.inDegrees());
  a.Wrap();
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
}

GTEST(add) {
  auto a = Angle::FromDegrees(90);
  a += Angle::FromRadians(Pi()/2.0f);
  EXPECT_FLOAT_EQ(180.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(180.0f, (Angle::FromDegrees(90) + Angle::FromRadians(Pi()/2.0f)).inDegrees());
}

GTEST(sub) {
  auto a = Angle::FromDegrees(180);
  a -= Angle::FromRadians(Pi()/2.0f);
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(90.0f, (Angle::FromDegrees(180) - Angle::FromRadians(Pi()/2.0f)).inDegrees());
}

GTEST(multi) {
  auto a = Angle::FromDegrees(90);
  a *= 2.0f;
  EXPECT_FLOAT_EQ(180.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(180.0f, (Angle::FromDegrees(90) * 2.0f).inDegrees());
  EXPECT_FLOAT_EQ(180.0f, (2.0f * Angle::FromDegrees(90)).inDegrees());
}

GTEST(div) {
  auto a = Angle::FromDegrees(180);
  a /= 2;
  EXPECT_FLOAT_EQ(90.0f, a.inDegrees());
  EXPECT_FLOAT_EQ(90.0f, (Angle::FromDegrees(180) / 2.0f).inDegrees());
}

struct KnownConstantsFromWikipedia : public ::testing::Test {
  // https://en.wikipedia.org/wiki/Trigonometric_functions#Explicit_values
  // https://en.wikipedia.org/wiki/Trigonometric_constants_expressed_in_real_radicals#Table_of_some_common_angles
  const float a = (Sqrt(6.0f) - Sqrt(2.0f)) / 4.0f;
  const float b = (Sqrt(2.0f) + Sqrt(6.0f)) / 4.0f;
};

const float NEAR  = 0.0000001f;
const float NEAR2 = 0.000001f;
const float NEAR3 = 0.00001f;
const float NEAR4 = 0.0001f;

TEST_F(KnownConstantsFromWikipedia, sin) {
  EXPECT_NEAR(0.0f, Sin(Angle::FromDegrees(0.0f)), NEAR);
  EXPECT_NEAR(1.0f, Sin(Angle::FromDegrees(90.0f)), NEAR);
  EXPECT_NEAR(0.0f, Sin(Angle::FromDegrees(180.0f)), NEAR);
  EXPECT_NEAR(0.5f, Sin(Angle::FromDegrees(30.0f)), NEAR);
  EXPECT_NEAR(a, Sin(Angle::FromDegrees(15.0f)), NEAR);
  EXPECT_NEAR(b, Sin(Angle::FromDegrees(75.0f)), NEAR);
}

TEST_F(KnownConstantsFromWikipedia, cos) {
  EXPECT_NEAR(1.0f, Cos(Angle::FromDegrees(0.0f)), NEAR);
  EXPECT_NEAR(0.0f, Cos(Angle::FromDegrees(90.0f)), NEAR);
  EXPECT_NEAR(-1.0f, Cos(Angle::FromDegrees(180.0f)), NEAR);
  EXPECT_NEAR(0.5f, Cos(Angle::FromDegrees(60.0f)), NEAR);
  EXPECT_NEAR(b, Cos(Angle::FromDegrees(15.0f)), NEAR);
  EXPECT_NEAR(a, Cos(Angle::FromDegrees(75.0f)), NEAR);
}

GTEST(tan) {
  EXPECT_NEAR(0.0f, Tan(Angle::FromDegrees(0.0f)), NEAR);
  EXPECT_NEAR(1.0f, Tan(Angle::FromDegrees(45.0f)), NEAR);
  EXPECT_NEAR(0.0f, Tan(Angle::FromDegrees(180.0f)), NEAR);
  EXPECT_NEAR(1.0f, Tan(Angle::FromDegrees(225.0f)), NEAR2);
  EXPECT_NEAR(Sqrt(3.0f), Tan(Angle::FromDegrees(60.0f)), NEAR2);
}

TEST_F(KnownConstantsFromWikipedia, asin) {
  EXPECT_NEAR(0.0f, Asin(0.0f).inDegrees(), NEAR);
  EXPECT_NEAR(90.0f, Asin(1.0f).inDegrees(), NEAR3);
  EXPECT_NEAR(30.0f, Asin(0.5f).inDegrees(), NEAR);
  EXPECT_NEAR(15.0f, Asin(a).inDegrees(), NEAR2);
  EXPECT_NEAR(75.0f, Asin(b).inDegrees(), NEAR3);
}

TEST_F(KnownConstantsFromWikipedia, acos) {
  EXPECT_NEAR(0.0f, Acos(1.0f).inDegrees(), NEAR);
  EXPECT_NEAR(90.0f, Acos(0.0f).inDegrees(), NEAR);
  EXPECT_NEAR(180.0f, Acos(-1.0f).inDegrees(), NEAR4);
  EXPECT_NEAR(60.0f, Acos(0.5f).inDegrees(), NEAR3);
  EXPECT_NEAR(15.0f, Acos(b).inDegrees(), NEAR3);
  EXPECT_NEAR(75.0f, Acos(a).inDegrees(), NEAR3);
}

GTEST(atan) {
  EXPECT_NEAR(0, Atan(0.0f).inDegrees(), NEAR);
  EXPECT_NEAR(45.0f, Atan(1.0f).inDegrees(), NEAR);
  EXPECT_NEAR(60.0f, Atan(Sqrt(3.0f)).inDegrees(), NEAR2);
}<|endoftext|>


<commit_before>#include "decrypt.h"

Tag5::Ptr find_decrypting_key(const PGP & k, const std::string &keyid){
    if (k.get_ASCII_Armor() == 2){
        std::vector <Packet::Ptr> packets = k.get_packets();
        for(Packet::Ptr const & p : packets){
            if ((p -> get_tag() == 5) || (p -> get_tag() == 7)){
                std::string data = p -> raw();
                Tag5::Ptr key = std::make_shared<Tag5>(data);
                if ( key->get_public_ptr()->get_keyid() != keyid ){
                    continue;
                }
                // make sure key has signing material
                if ((key -> get_pka() == 1) || // RSA
                    (key -> get_pka() == 2) || // RSA
                    (key -> get_pka() == 16)){ // ElGamal
                        return key;
                }
            }
        }
    }
    return Tag5::Ptr();
}

std::string pka_decrypt(const uint8_t pka, std::vector <PGPMPI> & data, const std::vector <PGPMPI> & pri, const std::vector <PGPMPI> & pub){
    if (pka < 3){   // RSA
        return mpitoraw(RSA_decrypt(data[0], pri, pub));
    }
    if (pka == 16){ // ElGamal
        return ElGamal_decrypt(data, pri, pub);
    }
    else{
        std::stringstream s; s << static_cast <int> (pka);
        throw std::runtime_error("Error: PKA number " + s.str() + " not allowed or unknown.");
    }
    return ""; // should never reach here; mainly just to remove compiler warnings
}

std::vector <PGPMPI> decrypt_secret_key(const Tag5::Ptr & pri, const std::string & passphrase){
    std::vector <PGPMPI> out;
    S2K::Ptr s2k = pri -> get_s2k();

    // calculate key used in encryption algorithm
    std::string key = s2k -> run(passphrase, Symmetric_Algorithm_Key_Length.at(Symmetric_Algorithms.at(pri -> get_sym())) >> 3);

    // decrypt secret key
    std::string secret_key = use_normal_CFB_decrypt(pri -> get_sym(), pri -> get_secret(), key, pri -> get_IV());
    // get checksum and remove it from the string
    const unsigned int hash_size = (pri -> get_s2k_con() == 254)?20:2;
    std::string checksum = secret_key.substr(secret_key.size() - hash_size, hash_size);
    secret_key = secret_key.substr(0, secret_key.size() - hash_size);

    // calculate and check checksum
    if(pri -> get_s2k_con() == 254){
        if (use_hash(s2k -> get_hash(), secret_key) != checksum){
            throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
        }
    }
    else{ // all other values; **UNTESTED**
        uint16_t sum = 0;
        for(char & c : secret_key){
            sum += static_cast <unsigned char> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){
            if (use_hash(s2k -> get_hash(), secret_key) != checksum){
                throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
            }
        }
    }

    // extract MPI values
    while (secret_key.size()){
        out.push_back(read_MPI(secret_key));
    }
    return out;
}

std::string decrypt_message(PGP & m, PGP& pri, const std::string & passphrase){
    if ((m.get_ASCII_Armor() != 0)/* && (m.get_ASCII_Armor() != 3) && (m.get_ASCII_Armor() != 4)*/){
        throw std::runtime_error("Error: No encrypted message found.");
    }

    if (pri.get_ASCII_Armor() != 2){
        throw std::runtime_error("Error: No Private Key found.");
    }
    // reused variables
    uint8_t packet;
    std::string data;
    std::string checksum;

    std::string session_key;                    // session key
    uint8_t sym;                                // symmetric key algorithm used to encrypt original data
    unsigned int BS;                            // blocksize of symmetric key algorithm

    // find session key
    std::vector <Packet::Ptr> message_packets = m.get_packets();
    for(Packet::Ptr const & p : message_packets){
        if ((p -> get_tag() == 1) || (p -> get_tag() == 3)){
            data = p -> raw();
            packet = p -> get_tag();
            break;
        }
    }

    if (packet == 1){ // Public-Key Encrypted Session Key Packet (Tag 1)
        Tag1 tag1(data);
        uint8_t pka = tag1.get_pka();
        std::vector <PGPMPI> session_key_mpi = tag1.get_mpi();

        // find corresponding secret key
        Tag5::Ptr sec = find_decrypting_key(pri, tag1.get_keyid());

        if (!sec){
            throw std::runtime_error("Error: Correct Private Key not found.");
        }

        std::vector <PGPMPI> pub = sec -> get_mpi();
        std::vector <PGPMPI> pri = decrypt_secret_key(sec, passphrase);

        // get session key
        session_key = zero + pka_decrypt(pka, session_key_mpi, pri, pub);                   // symmetric algorithm, session key, 2 octet checksum wrapped in EME_PKCS1_ENCODE
        session_key = EME_PKCS1v1_5_DECODE(session_key);                                    // remove EME_PKCS1 encoding
        sym = session_key[0];                                                               // get symmetric algorithm
        checksum = session_key.substr(session_key.size() - 2, 2);                           // get 2 octet checksum
        session_key = session_key.substr(1, session_key.size() - 3);                        // remove both from session key
        uint16_t sum = 0;
        for(char & c : session_key){                                                        // calculate session key checksum
            sum += static_cast <uint8_t> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){                                        // check session key checksums
            throw std::runtime_error("Error: Calculated session key checksum does not match given checksum.");
        }
    }
    else if (packet == 3){ //Symmetric-Key Encrypted Session Key Packet (Tag 3)
        /* untested */
        Tag3 tag3(data);
        data = tag3.get_key(passphrase);
        sym = data[0];
        session_key = data.substr(1, data.size() - 1);
    }
    else{
        throw std::runtime_error("Error: No session key packet found.");
    }

    BS = Symmetric_Algorithm_Block_Length.at(Symmetric_Algorithms.at(sym)) >> 3;           // get blocksize

    // Find encrypted data
    data = "";
    for(Packet::Ptr const & p : message_packets){
        if (p -> get_tag() == 9){
            data = p -> raw();
            Tag9 tag9(data);
            data = tag9.get_encrypted_data();
            packet = 9;
            break;
        }
        else if (p -> get_tag() == 18){
            data = p -> raw();
            Tag18 tag18(data);
            data = tag18.get_protected_data();
            packet = 18;
            break;
        }
    }
    if (!data.size()){
        throw std::runtime_error("Error: No encrypted data packets found.");
    }

    if (sym == 2){ // Triple DES
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key.substr(0, 8), session_key.substr(8, 8), session_key.substr(16, 8)); // decrypt encrypted data
    }
    else{
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key); // decrypt encrypted data
    }

    if (packet == 18){ // Symmetrically Encrypted Integrity Protected Data Packet (Tag 18)
        checksum = data.substr(data.size() - 20, 20);       // get given SHA1 checksum
        data = data.substr(0, data.size() - 20);            // remove SHA1 checksum
        if (use_hash(2, data) != checksum){                 // check SHA1 checksum
            throw std::runtime_error("Error: Given checksum and calculated checksum do not match.");
        }
        data = data.substr(0, data.size() - 2);             // get rid of \xd3\x14
    }

    data = data.substr(BS + 2, data.size() - BS - 2);       // get rid of prefix

    if (packet == 9){ // Symmetrically Encrypted Data Packet (Tag 9)
        // figure out which compression algorithm was used
        // uncompressed literal data packet
        if ((data[0] == 'b') || (data[0] == 't') || (data[0] == 'u')){
            data = Tag11(data).write(); // add in Tag11 headers to be removed later
        }
        // BZIP2
        else if (data.substr(0, 2) == "BZ"){
            data = PGP_decompress(3, data);
        }
        // ZLIB
        else if ((data.substr(0, 2) == "\x78\x01") || (data.substr(0, 2) == "\x78\x9c") || (data.substr(0, 2) == "\x78\xda")){
            data = PGP_decompress(2, data);
        }
        // DEFLATE
        else{
            data = PGP_decompress(1, data);
        }
    }

    // get rid of header and figure out what type of packet data it is
    bool format;
    data = read_packet_header(data, packet, format);

    // output data
    switch (packet){
        case 8: // Compressed Data Packet
            {
                data = Tag8(data).get_data(); // compressed packets
                std::vector <Packet::Ptr> compressed_packets;

                while (data.size()){ // extract packets
                    compressed_packets.push_back(read_packet(data));
                }

                data = ""; // 'data' should be empty at this point; clear it out just in case

                // extract all packet data; probably needs better formatting
                for(Packet::Ptr & p : compressed_packets){
                    std::string packet_data = p -> raw();
                    if (p -> get_tag() == 11){
                        Tag11 tag11(packet_data);
                        if (tag11.get_filename() == ""){
                            data += tag11.get_literal();
                        }
                        else{
                            tag11.out();
                            data += "Data written to file '" + tag11.get_filename() + "'";
                        }
                    }
                    // else{
                        // data += p -> show() + "\n";
                    // }
                }
            }
            break;
        case 11: // Literal Data Packet
            {
                Tag11 tag11(data);
                if (tag11.get_filename() == ""){
                    data = tag11.get_literal();
                }
                else{
                    tag11.write();
                    data = "Data written to file '" + Tag11(data).get_filename() + "'";
                }
            }
            break;
        default:
            {
                std::stringstream s; s << packet;
                throw std::runtime_error("Error: No action defined for packet type " + s.str());
            }
            break;
    }
    return data;
}
<commit_msg>Output string instead of writing to file, for now<commit_after>#include "decrypt.h"

Tag5::Ptr find_decrypting_key(const PGP & k, const std::string &keyid){
    if (k.get_ASCII_Armor() == 2){
        std::vector <Packet::Ptr> packets = k.get_packets();
        for(Packet::Ptr const & p : packets){
            if ((p -> get_tag() == 5) || (p -> get_tag() == 7)){
                std::string data = p -> raw();
                Tag5::Ptr key = std::make_shared<Tag5>(data);
                if ( key->get_public_ptr()->get_keyid() != keyid ){
                    continue;
                }
                // make sure key has signing material
                if ((key -> get_pka() == 1) || // RSA
                    (key -> get_pka() == 2) || // RSA
                    (key -> get_pka() == 16)){ // ElGamal
                        return key;
                }
            }
        }
    }
    return Tag5::Ptr();
}

std::string pka_decrypt(const uint8_t pka, std::vector <PGPMPI> & data, const std::vector <PGPMPI> & pri, const std::vector <PGPMPI> & pub){
    if (pka < 3){   // RSA
        return mpitoraw(RSA_decrypt(data[0], pri, pub));
    }
    if (pka == 16){ // ElGamal
        return ElGamal_decrypt(data, pri, pub);
    }
    else{
        std::stringstream s; s << static_cast <int> (pka);
        throw std::runtime_error("Error: PKA number " + s.str() + " not allowed or unknown.");
    }
    return ""; // should never reach here; mainly just to remove compiler warnings
}

std::vector <PGPMPI> decrypt_secret_key(const Tag5::Ptr & pri, const std::string & passphrase){
    std::vector <PGPMPI> out;
    S2K::Ptr s2k = pri -> get_s2k();

    // calculate key used in encryption algorithm
    std::string key = s2k -> run(passphrase, Symmetric_Algorithm_Key_Length.at(Symmetric_Algorithms.at(pri -> get_sym())) >> 3);

    // decrypt secret key
    std::string secret_key = use_normal_CFB_decrypt(pri -> get_sym(), pri -> get_secret(), key, pri -> get_IV());
    // get checksum and remove it from the string
    const unsigned int hash_size = (pri -> get_s2k_con() == 254)?20:2;
    std::string checksum = secret_key.substr(secret_key.size() - hash_size, hash_size);
    secret_key = secret_key.substr(0, secret_key.size() - hash_size);

    // calculate and check checksum
    if(pri -> get_s2k_con() == 254){
        if (use_hash(s2k -> get_hash(), secret_key) != checksum){
            throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
        }
    }
    else{ // all other values; **UNTESTED**
        uint16_t sum = 0;
        for(char & c : secret_key){
            sum += static_cast <unsigned char> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){
            if (use_hash(s2k -> get_hash(), secret_key) != checksum){
                throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
            }
        }
    }

    // extract MPI values
    while (secret_key.size()){
        out.push_back(read_MPI(secret_key));
    }
    return out;
}

std::string decrypt_message(PGP & m, PGP& pri, const std::string & passphrase){
    if ((m.get_ASCII_Armor() != 0)/* && (m.get_ASCII_Armor() != 3) && (m.get_ASCII_Armor() != 4)*/){
        throw std::runtime_error("Error: No encrypted message found.");
    }

    if (pri.get_ASCII_Armor() != 2){
        throw std::runtime_error("Error: No Private Key found.");
    }
    // reused variables
    uint8_t packet;
    std::string data;
    std::string checksum;

    std::string session_key;                    // session key
    uint8_t sym;                                // symmetric key algorithm used to encrypt original data
    unsigned int BS;                            // blocksize of symmetric key algorithm

    // find session key
    std::vector <Packet::Ptr> message_packets = m.get_packets();
    for(Packet::Ptr const & p : message_packets){
        if ((p -> get_tag() == 1) || (p -> get_tag() == 3)){
            data = p -> raw();
            packet = p -> get_tag();
            break;
        }
    }

    if (packet == 1){ // Public-Key Encrypted Session Key Packet (Tag 1)
        Tag1 tag1(data);
        uint8_t pka = tag1.get_pka();
        std::vector <PGPMPI> session_key_mpi = tag1.get_mpi();

        // find corresponding secret key
        Tag5::Ptr sec = find_decrypting_key(pri, tag1.get_keyid());

        if (!sec){
            throw std::runtime_error("Error: Correct Private Key not found.");
        }

        std::vector <PGPMPI> pub = sec -> get_mpi();
        std::vector <PGPMPI> pri = decrypt_secret_key(sec, passphrase);

        // get session key
        session_key = zero + pka_decrypt(pka, session_key_mpi, pri, pub);                   // symmetric algorithm, session key, 2 octet checksum wrapped in EME_PKCS1_ENCODE
        session_key = EME_PKCS1v1_5_DECODE(session_key);                                    // remove EME_PKCS1 encoding
        sym = session_key[0];                                                               // get symmetric algorithm
        checksum = session_key.substr(session_key.size() - 2, 2);                           // get 2 octet checksum
        session_key = session_key.substr(1, session_key.size() - 3);                        // remove both from session key
        uint16_t sum = 0;
        for(char & c : session_key){                                                        // calculate session key checksum
            sum += static_cast <uint8_t> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){                                        // check session key checksums
            throw std::runtime_error("Error: Calculated session key checksum does not match given checksum.");
        }
    }
    else if (packet == 3){ //Symmetric-Key Encrypted Session Key Packet (Tag 3)
        /* untested */
        Tag3 tag3(data);
        data = tag3.get_key(passphrase);
        sym = data[0];
        session_key = data.substr(1, data.size() - 1);
    }
    else{
        throw std::runtime_error("Error: No session key packet found.");
    }

    BS = Symmetric_Algorithm_Block_Length.at(Symmetric_Algorithms.at(sym)) >> 3;           // get blocksize

    // Find encrypted data
    data = "";
    for(Packet::Ptr const & p : message_packets){
        if (p -> get_tag() == 9){
            data = p -> raw();
            Tag9 tag9(data);
            data = tag9.get_encrypted_data();
            packet = 9;
            break;
        }
        else if (p -> get_tag() == 18){
            data = p -> raw();
            Tag18 tag18(data);
            data = tag18.get_protected_data();
            packet = 18;
            break;
        }
    }
    if (!data.size()){
        throw std::runtime_error("Error: No encrypted data packets found.");
    }

    if (sym == 2){ // Triple DES
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key.substr(0, 8), session_key.substr(8, 8), session_key.substr(16, 8)); // decrypt encrypted data
    }
    else{
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key); // decrypt encrypted data
    }

    if (packet == 18){ // Symmetrically Encrypted Integrity Protected Data Packet (Tag 18)
        checksum = data.substr(data.size() - 20, 20);       // get given SHA1 checksum
        data = data.substr(0, data.size() - 20);            // remove SHA1 checksum
        if (use_hash(2, data) != checksum){                 // check SHA1 checksum
            throw std::runtime_error("Error: Given checksum and calculated checksum do not match.");
        }
        data = data.substr(0, data.size() - 2);             // get rid of \xd3\x14
    }

    data = data.substr(BS + 2, data.size() - BS - 2);       // get rid of prefix

    if (packet == 9){ // Symmetrically Encrypted Data Packet (Tag 9)
        // figure out which compression algorithm was used
        // uncompressed literal data packet
        if ((data[0] == 'b') || (data[0] == 't') || (data[0] == 'u')){
            data = Tag11(data).write(); // add in Tag11 headers to be removed later
        }
        // BZIP2
        else if (data.substr(0, 2) == "BZ"){
            data = PGP_decompress(3, data);
        }
        // ZLIB
        else if ((data.substr(0, 2) == "\x78\x01") || (data.substr(0, 2) == "\x78\x9c") || (data.substr(0, 2) == "\x78\xda")){
            data = PGP_decompress(2, data);
        }
        // DEFLATE
        else{
            data = PGP_decompress(1, data);
        }
    }

    // get rid of header and figure out what type of packet data it is
    bool format;
    data = read_packet_header(data, packet, format);

    // output data
    switch (packet){
        case 8: // Compressed Data Packet
            {
                data = Tag8(data).get_data(); // compressed packets
                std::vector <Packet::Ptr> compressed_packets;

                while (data.size()){ // extract packets
                    compressed_packets.push_back(read_packet(data));
                }

                data = ""; // 'data' should be empty at this point; clear it out just in case

                // extract all packet data; probably needs better formatting
                for(Packet::Ptr & p : compressed_packets){
                    std::string packet_data = p -> raw();
                    if (p -> get_tag() == 11){
                        Tag11 tag11(packet_data);
                        // if (tag11.get_filename() == ""){
                            data += tag11.get_literal();
                        // }
                        // else{
                            // tag11.out();
                            // data += "Data written to file '" + tag11.get_filename() + "'";
                        // }
                    }
                    // else{
                        // data += p -> show() + "\n";
                    // }
                }
            }
            break;
        case 11: // Literal Data Packet
            {
                Tag11 tag11(data);
                if (tag11.get_filename() == ""){
                    data = tag11.get_literal();
                }
                else{
                    tag11.write();
                    data = "Data written to file '" + Tag11(data).get_filename() + "'";
                }
            }
            break;
        default:
            {
                std::stringstream s; s << packet;
                throw std::runtime_error("Error: No action defined for packet type " + s.str());
            }
            break;
    }
    return data;
}
<|endoftext|>


<commit_before>/**
 * @file kmeans.hpp
 * @author Parikshit Ram (pram@cc.gatech.edu)
 *
 * K-Means clustering.
 */
#ifndef __MLPACK_METHODS_KMEANS_KMEANS_HPP
#define __MLPACK_METHODS_KMEANS_KMEANS_HPP

#include <mlpack/core.hpp>

#include <mlpack/core/metrics/lmetric.hpp>
#include "random_partition.hpp"
#include "max_variance_new_cluster.hpp"

#include <mlpack/core/tree/binary_space_tree.hpp>

namespace mlpack {
namespace kmeans /** K-Means clustering. */ {

/**
 * This class implements K-Means clustering.  This implementation supports
 * overclustering, which means that more clusters than are requested will be
 * found; then, those clusters will be merged together to produce the desired
 * number of clusters.
 *
 * Two template parameters can (optionally) be supplied: the policy for how to
 * find the initial partition of the data, and the actions to be taken when an
 * empty cluster is encountered, as well as the distance metric to be used.
 *
 * A simple example of how to run K-Means clustering is shown below.
 *
 * @code
 * extern arma::mat data; // Dataset we want to run K-Means on.
 * arma::Col<size_t> assignments; // Cluster assignments.
 *
 * KMeans<> k(); // Default options.
 * k.Cluster(data, 3, assignments); // 3 clusters.
 *
 * // Cluster using the Manhattan distance, 100 iterations maximum, and an
 * // overclustering factor of 4.0.
 * KMeans<metric::ManhattanDistance> k(100, 4.0);
 * k.Cluster(data, 6, assignments); // 6 clusters.
 * @endcode
 *
 * @tparam DistanceMetric The distance metric to use for this KMeans; see
 *     metric::LMetric for an example.
 * @tparam InitialPartitionPolicy Initial partitioning policy; must implement a
 *     default constructor and 'void Cluster(const arma::mat&, const size_t,
 *     arma::Col<size_t>&)'.  @see RandomPartition for an example.
 * @tparam EmptyClusterPolicy Policy for what to do on an empty cluster; must
 *     implement a default constructor and 'void EmptyCluster(const arma::mat&,
 *     arma::Col<size_t&)'.  @see AllowEmptyClusters and MaxVarianceNewCluster.
 */
template<typename DistanceMetric = metric::SquaredEuclideanDistance,
         typename InitialPartitionPolicy = RandomPartition,
         typename EmptyClusterPolicy = MaxVarianceNewCluster>
class KMeans
{
 public:
  /**
   * Create a K-Means object and (optionally) set the parameters which K-Means
   * will be run with.  This implementation allows a few strategies to improve
   * the performance of K-Means, including "overclustering" and disallowing
   * empty clusters.
   *
   * The overclustering factor controls how many clusters are
   * actually found; for instance, with an overclustering factor of 4, if
   * K-Means is run to find 3 clusters, it will actually find 12, then merge the
   * nearest clusters until only 3 are left.
   *
   * @param maxIterations Maximum number of iterations allowed before giving up
   *     (0 is valid, but the algorithm may never terminate).
   * @param overclusteringFactor Factor controlling how many extra clusters are
   *     found and then merged to get the desired number of clusters.
   * @param metric Optional DistanceMetric object; for when the metric has state
   *     it needs to store.
   * @param partitioner Optional InitialPartitionPolicy object; for when a
   *     specially initialized partitioning policy is required.
   * @param emptyClusterAction Optional EmptyClusterPolicy object; for when a
   *     specially initialized empty cluster policy is required.
   */
  KMeans(const size_t maxIterations = 1000,
         const double overclusteringFactor = 1.0,
         const DistanceMetric metric = DistanceMetric(),
         const InitialPartitionPolicy partitioner = InitialPartitionPolicy(),
         const EmptyClusterPolicy emptyClusterAction = EmptyClusterPolicy());

  /**
   * Perform K-Means clustering on the data, returning a list of cluster
   * assignments.  Optionally, the vector of assignments can be set to an
   * initial guess of the cluster assignments; to do this, the number of
   * elements in the list of assignments must be equal to the number of points
   * (columns) in the dataset.
   *
   * @tparam MatType Type of matrix (arma::mat or arma::spmat).
   * @param data Dataset to cluster.
   * @param clusters Number of clusters to compute.
   * @param assignments Vector to store cluster assignments in.  Can contain an
   *     initial guess at cluster assignments.
   */
  template<typename MatType>
  void Cluster(const MatType& data,
               const size_t clusters,
               arma::Col<size_t>& assignments) const;
  template<typename MatType>
  void FastCluster(MatType& data,
               const size_t clusters,
               arma::Col<size_t>& assignments) const;

  /**
   * Return the overclustering factor.
   */
  double OverclusteringFactor() const { return overclusteringFactor; }

  /**
   * Set the overclustering factor.
   */
  void OverclusteringFactor(const double overclusteringFactor)
  {
    if (overclusteringFactor < 1.0)
    {
      Log::Warn << "KMeans::OverclusteringFactor(): invalid value (<= 1.0) "
          "ignored." << std::endl;
      return;
    }

    this->overclusteringFactor = overclusteringFactor;
  }

  /**
   * Get the maximum number of iterations.
   */
  size_t MaxIterations() const { return maxIterations; }

  /**
   * Set the maximum number of iterations.
   */
  void MaxIterations(const size_t maxIterations)
  {
    this->maxIterations = maxIterations;
  }

  //! Get the distance metric.
  const DistanceMetric& Metric() const { return metric; }
  //! Modify the distance metric.
  DistanceMetric& Metric() { return metric; }

  //! Get the initial partitioning policy.
  const InitialPartitionPolicy& Partitioner() const { return partitioner; }
  //! Modify the initial partitioning policy.
  InitialPartitionPolicy& Partitioner() { return partitioner; }

  //! Get the empty cluster policy.
  const EmptyClusterPolicy& EmptyClusterAction() const
  {
    return emptyClusterAction;
  }
  //! Modify the empty cluster policy.
  EmptyClusterPolicy& EmptyClusterAction() { return emptyClusterAction; }

 private:
  //! Factor controlling how many clusters are actually found.
  double overclusteringFactor;
  //! Maximum number of iterations before giving up.
  size_t maxIterations;
  //! Instantiated distance metric.
  DistanceMetric metric;
  //! Instantiated initial partitioning policy.
  InitialPartitionPolicy partitioner;
  //! Instantiated empty cluster policy.
  EmptyClusterPolicy emptyClusterAction;
};

}; // namespace kmeans
}; // namespace mlpack

// Include implementation.
#include "kmeans_impl.hpp"

#endif // __MLPACK_METHODS_MOG_KMEANS_HPP
<commit_msg>Oops, this is not the accessor/mutator design methodology we are using.<commit_after>/**
 * @file kmeans.hpp
 * @author Parikshit Ram (pram@cc.gatech.edu)
 *
 * K-Means clustering.
 */
#ifndef __MLPACK_METHODS_KMEANS_KMEANS_HPP
#define __MLPACK_METHODS_KMEANS_KMEANS_HPP

#include <mlpack/core.hpp>

#include <mlpack/core/metrics/lmetric.hpp>
#include "random_partition.hpp"
#include "max_variance_new_cluster.hpp"

#include <mlpack/core/tree/binary_space_tree.hpp>

namespace mlpack {
namespace kmeans /** K-Means clustering. */ {

/**
 * This class implements K-Means clustering.  This implementation supports
 * overclustering, which means that more clusters than are requested will be
 * found; then, those clusters will be merged together to produce the desired
 * number of clusters.
 *
 * Two template parameters can (optionally) be supplied: the policy for how to
 * find the initial partition of the data, and the actions to be taken when an
 * empty cluster is encountered, as well as the distance metric to be used.
 *
 * A simple example of how to run K-Means clustering is shown below.
 *
 * @code
 * extern arma::mat data; // Dataset we want to run K-Means on.
 * arma::Col<size_t> assignments; // Cluster assignments.
 *
 * KMeans<> k(); // Default options.
 * k.Cluster(data, 3, assignments); // 3 clusters.
 *
 * // Cluster using the Manhattan distance, 100 iterations maximum, and an
 * // overclustering factor of 4.0.
 * KMeans<metric::ManhattanDistance> k(100, 4.0);
 * k.Cluster(data, 6, assignments); // 6 clusters.
 * @endcode
 *
 * @tparam DistanceMetric The distance metric to use for this KMeans; see
 *     metric::LMetric for an example.
 * @tparam InitialPartitionPolicy Initial partitioning policy; must implement a
 *     default constructor and 'void Cluster(const arma::mat&, const size_t,
 *     arma::Col<size_t>&)'.  @see RandomPartition for an example.
 * @tparam EmptyClusterPolicy Policy for what to do on an empty cluster; must
 *     implement a default constructor and 'void EmptyCluster(const arma::mat&,
 *     arma::Col<size_t&)'.  @see AllowEmptyClusters and MaxVarianceNewCluster.
 */
template<typename DistanceMetric = metric::SquaredEuclideanDistance,
         typename InitialPartitionPolicy = RandomPartition,
         typename EmptyClusterPolicy = MaxVarianceNewCluster>
class KMeans
{
 public:
  /**
   * Create a K-Means object and (optionally) set the parameters which K-Means
   * will be run with.  This implementation allows a few strategies to improve
   * the performance of K-Means, including "overclustering" and disallowing
   * empty clusters.
   *
   * The overclustering factor controls how many clusters are
   * actually found; for instance, with an overclustering factor of 4, if
   * K-Means is run to find 3 clusters, it will actually find 12, then merge the
   * nearest clusters until only 3 are left.
   *
   * @param maxIterations Maximum number of iterations allowed before giving up
   *     (0 is valid, but the algorithm may never terminate).
   * @param overclusteringFactor Factor controlling how many extra clusters are
   *     found and then merged to get the desired number of clusters.
   * @param metric Optional DistanceMetric object; for when the metric has state
   *     it needs to store.
   * @param partitioner Optional InitialPartitionPolicy object; for when a
   *     specially initialized partitioning policy is required.
   * @param emptyClusterAction Optional EmptyClusterPolicy object; for when a
   *     specially initialized empty cluster policy is required.
   */
  KMeans(const size_t maxIterations = 1000,
         const double overclusteringFactor = 1.0,
         const DistanceMetric metric = DistanceMetric(),
         const InitialPartitionPolicy partitioner = InitialPartitionPolicy(),
         const EmptyClusterPolicy emptyClusterAction = EmptyClusterPolicy());

  /**
   * Perform K-Means clustering on the data, returning a list of cluster
   * assignments.  Optionally, the vector of assignments can be set to an
   * initial guess of the cluster assignments; to do this, the number of
   * elements in the list of assignments must be equal to the number of points
   * (columns) in the dataset.
   *
   * @tparam MatType Type of matrix (arma::mat or arma::spmat).
   * @param data Dataset to cluster.
   * @param clusters Number of clusters to compute.
   * @param assignments Vector to store cluster assignments in.  Can contain an
   *     initial guess at cluster assignments.
   */
  template<typename MatType>
  void Cluster(const MatType& data,
               const size_t clusters,
               arma::Col<size_t>& assignments) const;
  template<typename MatType>
  void FastCluster(MatType& data,
               const size_t clusters,
               arma::Col<size_t>& assignments) const;

  //! Return the overclustering factor.
  double OverclusteringFactor() const { return overclusteringFactor; }
  //! Set the overclustering factor.  Must be greater than 1.
  double& OverclusteringFactor() { return overclusteringFactor; }

  //! Get the maximum number of iterations.
  size_t MaxIterations() const { return maxIterations; }
  //! Set the maximum number of iterations.
  size_t& MaxIterations() { return maxIterations; }

  //! Get the distance metric.
  const DistanceMetric& Metric() const { return metric; }
  //! Modify the distance metric.
  DistanceMetric& Metric() { return metric; }

  //! Get the initial partitioning policy.
  const InitialPartitionPolicy& Partitioner() const { return partitioner; }
  //! Modify the initial partitioning policy.
  InitialPartitionPolicy& Partitioner() { return partitioner; }

  //! Get the empty cluster policy.
  const EmptyClusterPolicy& EmptyClusterAction() const
  { return emptyClusterAction; }
  //! Modify the empty cluster policy.
  EmptyClusterPolicy& EmptyClusterAction() { return emptyClusterAction; }

 private:
  //! Factor controlling how many clusters are actually found.
  double overclusteringFactor;
  //! Maximum number of iterations before giving up.
  size_t maxIterations;
  //! Instantiated distance metric.
  DistanceMetric metric;
  //! Instantiated initial partitioning policy.
  InitialPartitionPolicy partitioner;
  //! Instantiated empty cluster policy.
  EmptyClusterPolicy emptyClusterAction;
};

}; // namespace kmeans
}; // namespace mlpack

// Include implementation.
#include "kmeans_impl.hpp"

#endif // __MLPACK_METHODS_MOG_KMEANS_HPP
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2019 musikcube team
//
// All rights reserved.
//
// 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 author nor the names of other 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 <stdafx.h>
#include <algorithm>
#include <cursespp/Scrollbar.h>

using namespace cursespp;

void Scrollbar::Draw(ListWindow* list, Window* target) {
    int height = list->GetHeight();
    auto *adapter = &list->GetScrollAdapter();
    if (adapter && height > 2) {
        auto& pos = list->GetScrollPosition();

        /* these are defaults for drawing to an external view, that
        is, when target != list. */
        int xOffset = 0;
        int from = 0, to = height;
        WINDOW* frame = nullptr;
        float range = (float) height;
        size_t minY = 0;

        if (!target || target == list) {
            /* if we're drawing on top of the ListWindow's frame,
            we need to account for the padding it provides. */
            frame = list->GetFrame();
            xOffset = list->GetWidth() - 1;
            ++from; --to;
            range -= 2.0f;
            minY = 1;
        }
        else {
            frame = target->GetFrame();
        }

        float total = (float) std::max(minY, adapter->GetEntryCount());

        int yOffset;
        if (range >= total) {
            yOffset = -1;
        }
        else {
            float percent = (float) pos.logicalIndex / total;
            yOffset = (int) (range * percent) + minY;
        }

        mvwvline(frame, from, xOffset, 0, to - from);
        mvwaddch(frame, yOffset, xOffset, ' ' | A_REVERSE);
    }
}
<commit_msg>Cleaned up and added asserts to `Scrollbar.cpp`<commit_after>//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2004-2019 musikcube team
//
// All rights reserved.
//
// 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 author nor the names of other 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 <stdafx.h>
#include <algorithm>
#include <cursespp/Scrollbar.h>

using namespace cursespp;

void Scrollbar::Draw(ListWindow* list, Window* target) {
    auto *adapter = &list->GetScrollAdapter();
    if (adapter) {
        target = (target == nullptr) ? list : target;

        int trackHeight = target->GetHeight();

        if (target->IsFrameVisible()) {
            trackHeight -= 2; /* account for border */
        }

        if (trackHeight > 1) {
            auto& pos = list->GetScrollPosition();
            WINDOW* window = target->GetFrame();
            size_t itemCount = adapter->GetEntryCount();

            /* track */
            int trackX = target->GetWidth() - 1;
            int trackMinY = target->IsFrameVisible() ? 1 : 0;
            int trackMaxY = trackMinY + trackHeight;

            /* thumb */
            int thumbY = -1;
            if (itemCount > trackHeight) {
                float percent = (float) pos.logicalIndex / (float) itemCount;
                thumbY = (int) ((float) trackHeight * percent) + trackMinY;
            }

            /* validate */
            assert(trackMinY >= 0);
            assert(trackMaxY <= target->GetHeight());
            assert(trackMaxY > trackMinY);
            assert(trackHeight <= target->GetHeight());

            /* draw */
            mvwvline(window, trackMinY, trackX, 0, trackHeight); /* track */
            if (thumbY >= trackMinY && thumbY < trackMaxY) {
                mvwaddch(window, thumbY, trackX, ' ' | A_REVERSE); /* handle */
            }
        }
    }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.

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 Universite de Sherbrooke 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 HOLDER 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 <ros/ros.h>
#include <pluginlib/class_list_macros.h>
#include <nodelet/nodelet.h>

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl_conversions/pcl_conversions.h>

#include <tf/transform_listener.h>

#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <sensor_msgs/CameraInfo.h>
#include <stereo_msgs/DisparityImage.h>

#include <image_transport/image_transport.h>
#include <image_transport/subscriber_filter.h>

#include <image_geometry/pinhole_camera_model.h>

#include <message_filters/sync_policies/approximate_time.h>
#include <message_filters/subscriber.h>

#include <cv_bridge/cv_bridge.h>
#include <opencv2/highgui/highgui.hpp>

#include <rtabmap_ros/MsgConversion.h>

#include "rtabmap/core/util3d.h"
#include "rtabmap/core/util3d_filtering.h"
#include "rtabmap/core/util3d_mapping.h"
#include "rtabmap/core/util3d_transforms.h"

namespace rtabmap_ros
{

class ObstaclesDetection : public nodelet::Nodelet
{
public:
	ObstaclesDetection() :
		frameId_("base_link"),
		normalEstimationRadius_(0.05),
		groundNormalAngle_(M_PI_4),
		minClusterSize_(20),
		maxFloorHeight_(-1),
		maxObstaclesHeight_(1.5),
		waitForTransform_(false),
		simpleSegmentation_(false)
	{}

	virtual ~ObstaclesDetection()
	{}

private:
	virtual void onInit()
	{
		ros::NodeHandle & nh = getNodeHandle();
		ros::NodeHandle & pnh = getPrivateNodeHandle();

		int queueSize = 10;
		pnh.param("queue_size", queueSize, queueSize);
		pnh.param("frame_id", frameId_, frameId_);
		pnh.param("normal_estimation_radius", normalEstimationRadius_, normalEstimationRadius_);
		pnh.param("ground_normal_angle", groundNormalAngle_, groundNormalAngle_);
		pnh.param("min_cluster_size", minClusterSize_, minClusterSize_);
		pnh.param("max_obstacles_height", maxObstaclesHeight_, maxObstaclesHeight_);
		pnh.param("max_floor_height", maxFloorHeight_, maxFloorHeight_);
		pnh.param("wait_for_transform", waitForTransform_, waitForTransform_);
		pnh.param("simple_segmentation", simpleSegmentation_, simpleSegmentation_);

		cloudSub_ = nh.subscribe("cloud", 1, &ObstaclesDetection::callback, this);

		groundPub_ = nh.advertise<sensor_msgs::PointCloud2>("ground", 1);
		obstaclesPub_ = nh.advertise<sensor_msgs::PointCloud2>("obstacles", 1);

		this->_lastFrameTime = ros::Time::now();
	}

	void callback(const sensor_msgs::PointCloud2ConstPtr & cloudMsg)
	{
		if (groundPub_.getNumSubscribers() == 0 && obstaclesPub_.getNumSubscribers() == 0)
		{
			// no one wants the results
			return;
		}

		rtabmap::Transform localTransform;
		try
		{
			if(waitForTransform_)
			{
				if(!tfListener_.waitForTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, ros::Duration(1)))
				{
					ROS_ERROR("Could not get transform from %s to %s after 1 second!", frameId_.c_str(), cloudMsg->header.frame_id.c_str());
					return;
				}
			}

			tf::StampedTransform tmp;
			tfListener_.lookupTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, tmp);
			localTransform = rtabmap_ros::transformFromTF(tmp);
		}
		catch(tf::TransformException & ex)
		{
			ROS_ERROR("%s",ex.what());
			return;
		}

		pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::fromROSMsg(*cloudMsg, *originalCloud);
		if(originalCloud->size() == 0)
		{
			ROS_ERROR("Recieved empty point cloud!");
			return;
		}
		originalCloud = rtabmap::util3d::transformPointCloud(originalCloud, localTransform);

		/////////////////////////////////////////////////////////////////////////////

		pcl::PointCloud<pcl::PointXYZ>::Ptr hypotheticalGroundCloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::IndicesPtr ground, obstacles;
		pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(new pcl::PointCloud<pcl::PointXYZ>);

		ros::Time lasttime = ros::Time::now();

		hypotheticalGroundCloud = rtabmap::util3d::passThrough(originalCloud, "z", std::numeric_limits<int>::min(), maxFloorHeight_);

		if (!simpleSegmentation_){

			rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(hypotheticalGroundCloud,
					ground, obstacles, normalEstimationRadius_, groundNormalAngle_, minClusterSize_);

			if(ground.get() && ground->size())
			{
				pcl::copyPointCloud(*hypotheticalGroundCloud, *ground, *groundCloud);
			}

			obstaclesCloud = rtabmap::util3d::passThrough(originalCloud, "z", maxFloorHeight_, maxObstaclesHeight_);

			if(obstacles.get() && obstacles->size())
			{
				pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesNearFloorCloud(new pcl::PointCloud<pcl::PointXYZ>);
				pcl::copyPointCloud(*hypotheticalGroundCloud, *obstacles, *obstaclesNearFloorCloud);
				*obstaclesCloud += *obstaclesNearFloorCloud;
			}

		}
		else{
			obstaclesCloud = rtabmap::util3d::passThrough(originalCloud, "z", maxFloorHeight_, maxObstaclesHeight_);
		}

		if(groundPub_.getNumSubscribers())
		{
			sensor_msgs::PointCloud2 rosCloud;
			if (simpleSegmentation_) {pcl::toROSMsg(*hypotheticalGroundCloud, rosCloud);;}
			else {pcl::toROSMsg(*groundCloud, rosCloud);}
			rosCloud.header.stamp = cloudMsg->header.stamp;
			rosCloud.header.frame_id = frameId_;

			//publish the message
			groundPub_.publish(rosCloud);
		}

		if(obstaclesPub_.getNumSubscribers())
		{
			sensor_msgs::PointCloud2 rosCloud;
			pcl::toROSMsg(*obstaclesCloud, rosCloud);
			rosCloud.header.stamp = cloudMsg->header.stamp;
			rosCloud.header.frame_id = frameId_;

			//publish the message
			obstaclesPub_.publish(rosCloud);
		}

		ros::Time curtime = ros::Time::now();

		ros::Duration process_duration = curtime - lasttime;
		ros::Duration between_frames = curtime - this->_lastFrameTime;
		this->_lastFrameTime = curtime;
		//std::stringstream buffer;
		//buffer << "cloud=" << originalCloud->size() << " ground=" << hypotheticalGroundCloud->size() << " floor=" << ground->size() << " obst=" << obstacles->size();
		//buffer << " t=" << process_duration.toSec() << "s; " << (1./between_frames.toSec()) << "Hz";
		//ROS_ERROR("3%s: %s", this->getName().c_str(), buffer.str().c_str());

	}

private:
	std::string frameId_;
	double normalEstimationRadius_;
	double groundNormalAngle_;
	int minClusterSize_;
	double maxObstaclesHeight_;
	double maxFloorHeight_;
	bool waitForTransform_;
	bool simpleSegmentation_;

	tf::TransformListener tfListener_;

	ros::Publisher groundPub_;
	ros::Publisher obstaclesPub_;

	ros::Subscriber cloudSub_;
	ros::Time _lastFrameTime;
};

PLUGINLIB_EXPORT_CLASS(rtabmap_ros::ObstaclesDetection, nodelet::Nodelet);
}

<commit_msg>Publish even when pointcloud is null, otherwise system hangs<commit_after>/*
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.

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 Universite de Sherbrooke 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 HOLDER 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 <ros/ros.h>
#include <pluginlib/class_list_macros.h>
#include <nodelet/nodelet.h>

#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl_conversions/pcl_conversions.h>

#include <tf/transform_listener.h>

#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <sensor_msgs/CameraInfo.h>
#include <stereo_msgs/DisparityImage.h>

#include <image_transport/image_transport.h>
#include <image_transport/subscriber_filter.h>

#include <image_geometry/pinhole_camera_model.h>

#include <message_filters/sync_policies/approximate_time.h>
#include <message_filters/subscriber.h>

#include <cv_bridge/cv_bridge.h>
#include <opencv2/highgui/highgui.hpp>

#include <rtabmap_ros/MsgConversion.h>

#include "rtabmap/core/util3d.h"
#include "rtabmap/core/util3d_filtering.h"
#include "rtabmap/core/util3d_mapping.h"
#include "rtabmap/core/util3d_transforms.h"

namespace rtabmap_ros
{

class ObstaclesDetection : public nodelet::Nodelet
{
public:
	ObstaclesDetection() :
		frameId_("base_link"),
		normalEstimationRadius_(0.05),
		groundNormalAngle_(M_PI_4),
		minClusterSize_(20),
		maxFloorHeight_(-1),
		maxObstaclesHeight_(1.5),
		waitForTransform_(false),
		simpleSegmentation_(false)
	{}

	virtual ~ObstaclesDetection()
	{}

private:
	virtual void onInit()
	{
		ros::NodeHandle & nh = getNodeHandle();
		ros::NodeHandle & pnh = getPrivateNodeHandle();

		int queueSize = 10;
		pnh.param("queue_size", queueSize, queueSize);
		pnh.param("frame_id", frameId_, frameId_);
		pnh.param("normal_estimation_radius", normalEstimationRadius_, normalEstimationRadius_);
		pnh.param("ground_normal_angle", groundNormalAngle_, groundNormalAngle_);
		pnh.param("min_cluster_size", minClusterSize_, minClusterSize_);
		pnh.param("max_obstacles_height", maxObstaclesHeight_, maxObstaclesHeight_);
		pnh.param("max_floor_height", maxFloorHeight_, maxFloorHeight_);
		pnh.param("wait_for_transform", waitForTransform_, waitForTransform_);
		pnh.param("simple_segmentation", simpleSegmentation_, simpleSegmentation_);

		cloudSub_ = nh.subscribe("cloud", 1, &ObstaclesDetection::callback, this);

		groundPub_ = nh.advertise<sensor_msgs::PointCloud2>("ground", 1);
		obstaclesPub_ = nh.advertise<sensor_msgs::PointCloud2>("obstacles", 1);

		this->_lastFrameTime = ros::Time::now();
	}

	void callback(const sensor_msgs::PointCloud2ConstPtr & cloudMsg)
	{
		if (groundPub_.getNumSubscribers() == 0 && obstaclesPub_.getNumSubscribers() == 0)
		{
			// no one wants the results
			return;
		}

		rtabmap::Transform localTransform;
		try
		{
			if(waitForTransform_)
			{
				if(!tfListener_.waitForTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, ros::Duration(1)))
				{
					ROS_ERROR("Could not get transform from %s to %s after 1 second!", frameId_.c_str(), cloudMsg->header.frame_id.c_str());
					return;
				}
			}

			tf::StampedTransform tmp;
			tfListener_.lookupTransform(frameId_, cloudMsg->header.frame_id, cloudMsg->header.stamp, tmp);
			localTransform = rtabmap_ros::transformFromTF(tmp);
		}
		catch(tf::TransformException & ex)
		{
			ROS_ERROR("%s",ex.what());
			return;
		}

		pcl::PointCloud<pcl::PointXYZ>::Ptr originalCloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::fromROSMsg(*cloudMsg, *originalCloud);

		if(originalCloud->size() == 0)
		{
			ROS_ERROR("Recieved empty point cloud!");
			if(groundPub_.getNumSubscribers())
			{
				sensor_msgs::PointCloud2 rosCloud;
				pcl::toROSMsg(*originalCloud, rosCloud);
				rosCloud.header.stamp = cloudMsg->header.stamp;
				rosCloud.header.frame_id = frameId_;

				//publish the message
				groundPub_.publish(rosCloud);
			}

			if(obstaclesPub_.getNumSubscribers())
			{
				sensor_msgs::PointCloud2 rosCloud;
				pcl::toROSMsg(*originalCloud, rosCloud);
				rosCloud.header.stamp = cloudMsg->header.stamp;
				rosCloud.header.frame_id = frameId_;

				//publish the message
				obstaclesPub_.publish(rosCloud);
			}
			return;
		}

		originalCloud = rtabmap::util3d::transformPointCloud(originalCloud, localTransform);

		/////////////////////////////////////////////////////////////////////////////

		pcl::PointCloud<pcl::PointXYZ>::Ptr hypotheticalGroundCloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesCloud(new pcl::PointCloud<pcl::PointXYZ>);
		pcl::IndicesPtr ground, obstacles;
		pcl::PointCloud<pcl::PointXYZ>::Ptr groundCloud(new pcl::PointCloud<pcl::PointXYZ>);

		ros::Time lasttime = ros::Time::now();

		hypotheticalGroundCloud = rtabmap::util3d::passThrough(originalCloud, "z", std::numeric_limits<int>::min(), maxFloorHeight_);

		if (!simpleSegmentation_){

			rtabmap::util3d::segmentObstaclesFromGround<pcl::PointXYZ>(hypotheticalGroundCloud,
					ground, obstacles, normalEstimationRadius_, groundNormalAngle_, minClusterSize_);

			if(ground.get() && ground->size())
			{
				pcl::copyPointCloud(*hypotheticalGroundCloud, *ground, *groundCloud);
			}

			obstaclesCloud = rtabmap::util3d::passThrough(originalCloud, "z", maxFloorHeight_, maxObstaclesHeight_);

			if(obstacles.get() && obstacles->size())
			{
				pcl::PointCloud<pcl::PointXYZ>::Ptr obstaclesNearFloorCloud(new pcl::PointCloud<pcl::PointXYZ>);
				pcl::copyPointCloud(*hypotheticalGroundCloud, *obstacles, *obstaclesNearFloorCloud);
				*obstaclesCloud += *obstaclesNearFloorCloud;
			}

		}
		else{
			obstaclesCloud = rtabmap::util3d::passThrough(originalCloud, "z", maxFloorHeight_, maxObstaclesHeight_);
		}

		if(groundPub_.getNumSubscribers())
		{
			sensor_msgs::PointCloud2 rosCloud;
			if (simpleSegmentation_) {pcl::toROSMsg(*hypotheticalGroundCloud, rosCloud);;}
			else {pcl::toROSMsg(*groundCloud, rosCloud);}
			rosCloud.header.stamp = cloudMsg->header.stamp;
			rosCloud.header.frame_id = frameId_;

			//publish the message
			groundPub_.publish(rosCloud);
		}

		if(obstaclesPub_.getNumSubscribers())
		{
			sensor_msgs::PointCloud2 rosCloud;
			pcl::toROSMsg(*obstaclesCloud, rosCloud);
			rosCloud.header.stamp = cloudMsg->header.stamp;
			rosCloud.header.frame_id = frameId_;

			//publish the message
			obstaclesPub_.publish(rosCloud);
		}

		ros::Time curtime = ros::Time::now();

		ros::Duration process_duration = curtime - lasttime;
		ros::Duration between_frames = curtime - this->_lastFrameTime;
		this->_lastFrameTime = curtime;
		//std::stringstream buffer;
		//buffer << "cloud=" << originalCloud->size() << " ground=" << hypotheticalGroundCloud->size() << " floor=" << ground->size() << " obst=" << obstacles->size();
		//buffer << " t=" << process_duration.toSec() << "s; " << (1./between_frames.toSec()) << "Hz";
		//ROS_ERROR("3%s: %s", this->getName().c_str(), buffer.str().c_str());

	}

private:
	std::string frameId_;
	double normalEstimationRadius_;
	double groundNormalAngle_;
	int minClusterSize_;
	double maxObstaclesHeight_;
	double maxFloorHeight_;
	bool waitForTransform_;
	bool simpleSegmentation_;

	tf::TransformListener tfListener_;

	ros::Publisher groundPub_;
	ros::Publisher obstaclesPub_;

	ros::Subscriber cloudSub_;
	ros::Time _lastFrameTime;
};

PLUGINLIB_EXPORT_CLASS(rtabmap_ros::ObstaclesDetection, nodelet::Nodelet);
}

<|endoftext|>


<commit_before>
// Copyright (c) 2015 Pierre MOULON.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//-----------------
// Test summary:
//-----------------
// - Create a SfM_Data scene from a synthetic dataset
//   - since random noise have been added on 2d data point (initial residual is not small)
// - Check that residual is small once the generic Bundle Adjustment framework have been called.
// --
// - Perform the test for all the plausible intrinsic camera models
//-----------------

#include "openMVG/multiview/test_data_sets.hpp"
#include "openMVG/sfm/sfm.hpp"
using namespace openMVG;
using namespace openMVG::cameras;
using namespace openMVG::geometry;
using namespace openMVG::sfm;

#include "testing/testing.h"
#include "../cameras/Camera_Common.hpp"

#include <cmath>
#include <cstdio>
#include <iostream>

double RMSE(const SfM_Data & sfm_data);

SfM_Data getInputScene(const NViewDataSet & d, const nViewDatasetConfigurator & config, EINTRINSIC eintrinsic);

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Radial_K1) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_RADIAL1);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Radial_K3) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_RADIAL3);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Intrinsic_Brown_T2) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_BROWN);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Intrinsic_Fisheye) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_FISHEYE);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

/// Compute the Root Mean Square Error of the residuals
double RMSE(const SfM_Data & sfm_data)
{
  // Compute residuals for each observation
  std::vector<double> vec;
  for(Landmarks::const_iterator iterTracks = sfm_data.GetLandmarks().begin();
      iterTracks != sfm_data.GetLandmarks().end();
      ++iterTracks)
  {
    const Observations & obs = iterTracks->second.obs;
    for(Observations::const_iterator itObs = obs.begin();
      itObs != obs.end(); ++itObs)
    {
      const View * view = sfm_data.GetViews().find(itObs->first)->second.get();
      const Pose3 pose = sfm_data.GetPoseOrDie(view);
      const std::shared_ptr<IntrinsicBase> intrinsic = sfm_data.GetIntrinsics().find(view->id_intrinsic)->second;
      const Vec2 residual = intrinsic->residual(pose, iterTracks->second.X, itObs->second.x);
      vec.push_back( residual(0) );
      vec.push_back( residual(1) );
    }
  }
  const Eigen::Map<Eigen::RowVectorXd> residuals(&vec[0], vec.size());
  const double RMSE = std::sqrt(residuals.squaredNorm() / vec.size());
  return RMSE;
}

// Translation a synthetic scene into a valid SfM_Data scene.
// => A synthetic scene is used:
//    a random noise between [-.5,.5] is added on observed data points
SfM_Data getInputScene(const NViewDataSet & d, const nViewDatasetConfigurator & config, EINTRINSIC eintrinsic)
{
  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data;

  // 1. Views
  // 2. Poses
  // 3. Intrinsic data (shared, so only one camera intrinsic is defined)
  // 4. Landmarks

  const int nviews = d._C.size();
  const int npoints = d._X.cols();

  // 1. Views
  for (int i = 0; i < nviews; ++i)
  {
    const IndexT id_view = i, id_pose = i, id_intrinsic = 0; //(shared intrinsics)
    sfm_data.views[i] = std::make_shared<View>("", id_view, id_intrinsic, id_pose, config._cx *2, config._cy *2);
  }

  // 2. Poses
  for (int i = 0; i < nviews; ++i)
  {
    Pose3 pose(d._R[i], d._C[i]);
    sfm_data.poses[i] = pose;
  }

  // 3. Intrinsic data (shared, so only one camera intrinsic is defined)
  {
    const unsigned int w = config._cx *2;
    const unsigned int h = config._cy *2;
    switch (eintrinsic)
    {
      case PINHOLE_CAMERA:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic>
          (w, h, config._fx, config._cx, config._cy);
      break;
      case PINHOLE_CAMERA_RADIAL1:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Radial_K1>
          (w, h, config._fx, config._cx, config._cy, 0.0);
      break;
      case PINHOLE_CAMERA_RADIAL3:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Radial_K3>
          (w, h, config._fx, config._cx, config._cy, 0., 0., 0.);
      break;
      case PINHOLE_CAMERA_BROWN:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Brown_T2>
          (w, h, config._fx, config._cx, config._cy, 0., 0., 0., 0., 0.);
      break;
      case PINHOLE_CAMERA_FISHEYE:
      sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Fisheye>
          (w, h, config._fx, config._cx, config._cy, 0., 0., 0., 0.);
      break;
      default:
        std::cout << "Not yet supported" << std::endl;
    }
  }

  // 4. Landmarks
  for (int i = 0; i < npoints; ++i) {
    // Collect observation of the landmarks X in each frame.
    Landmark landmark;
    landmark.X = d._X.col(i);
    for (int j = 0; j < nviews; ++j) {
      Vec2 pt = d._x[j].col(i);
      // => random noise between [-.5,.5] is added
      pt(0) += rand()/RAND_MAX - .5;
      pt(1) += rand()/RAND_MAX - .5;

      landmark.obs[j] = Observation(pt, i);
    }
    sfm_data.structure[i] = landmark;
  }

  return sfm_data;
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
/* ************************************************************************* */
<commit_msg>[geodesy] Test GCP 3D similarity fitting #565 - Add an unit test covering the GCP registration. - Use standard c++11 random number generation.<commit_after>
// Copyright (c) 2015 Pierre MOULON.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//-----------------
// Test summary:
//-----------------
// - Create a SfM_Data scene from a synthetic dataset
//   - since random noise have been added on 2d data point (initial residual is not small)
// - Check that residual is small once the generic Bundle Adjustment framework have been called.
// --
// - Perform the test for all the plausible intrinsic camera models
//-----------------

#include "openMVG/multiview/test_data_sets.hpp"
#include "openMVG/sfm/sfm.hpp"
using namespace openMVG;
using namespace openMVG::cameras;
using namespace openMVG::geometry;
using namespace openMVG::sfm;

#include "testing/testing.h"
#include "../cameras/Camera_Common.hpp"

#include <cmath>
#include <cstdio>
#include <iostream>
#include <random>

double RMSE(const SfM_Data & sfm_data);

SfM_Data getInputScene
(
  const NViewDataSet & d,
  const nViewDatasetConfigurator & config,
  EINTRINSIC eintrinsic,
  const bool b_use_gcp = false
);

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Radial_K1) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_RADIAL1);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Radial_K3) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_RADIAL3);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Intrinsic_Brown_T2) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_BROWN);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_Intrinsic_Fisheye) {

  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA_FISHEYE);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::ADJUST_ALL,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL)) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);
}

//-- Test with GCP - Camera position once BA done must be as same as the GT
TEST(BUNDLE_ADJUSTMENT, EffectiveMinimization_Pinhole_GCP)
{
  const int nviews = 3;
  const int npoints = 6;
  const nViewDatasetConfigurator config;
  const NViewDataSet d = NRealisticCamerasRing(nviews, npoints, config);

  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data = getInputScene(d, config, PINHOLE_CAMERA, true);

  const double dResidual_before = RMSE(sfm_data);

  // Call the BA interface and let it refine (Structure and Camera parameters [Intrinsics|Motion])
  std::shared_ptr<Bundle_Adjustment> ba_object = std::make_shared<Bundle_Adjustment_Ceres>();
  EXPECT_TRUE( ba_object->Adjust(sfm_data,
    Optimize_Options(
      Intrinsic_Parameter_Type::NONE,
      Extrinsic_Parameter_Type::ADJUST_ALL,
      Structure_Parameter_Type::ADJUST_ALL,
      //-> Use GCP to fit the SfM scene to the GT coordinates system (Scale, Rotation, Translation)
      Control_Point_Parameter(20.0, true))) );

  const double dResidual_after = RMSE(sfm_data);
  EXPECT_TRUE( dResidual_before > dResidual_after);

  //-- Check camera pose are to the right place (since GCP was used, the camera coordinates must be the same)
  int cpt = 0;
  for (const auto & view_it : sfm_data.GetViews())
  {
    const Pose3 pose = sfm_data.GetPoseOrDie(view_it.second.get());
    const double position_residual = (d._C[cpt] - pose.center()).norm();
    EXPECT_NEAR(0.0, position_residual, 1e-4);
    ++cpt;
  }
}

/// Compute the Root Mean Square Error of the residuals
double RMSE(const SfM_Data & sfm_data)
{
  // Compute residuals for each observation
  std::vector<double> vec;
  for(Landmarks::const_iterator iterTracks = sfm_data.GetLandmarks().begin();
      iterTracks != sfm_data.GetLandmarks().end();
      ++iterTracks)
  {
    const Observations & obs = iterTracks->second.obs;
    for(Observations::const_iterator itObs = obs.begin();
      itObs != obs.end(); ++itObs)
    {
      const View * view = sfm_data.GetViews().find(itObs->first)->second.get();
      const Pose3 pose = sfm_data.GetPoseOrDie(view);
      const std::shared_ptr<IntrinsicBase> intrinsic = sfm_data.GetIntrinsics().find(view->id_intrinsic)->second;
      const Vec2 residual = intrinsic->residual(pose, iterTracks->second.X, itObs->second.x);
      vec.push_back( residual(0) );
      vec.push_back( residual(1) );
    }
  }
  const Eigen::Map<Eigen::RowVectorXd> residuals(&vec[0], vec.size());
  const double RMSE = std::sqrt(residuals.squaredNorm() / vec.size());
  return RMSE;
}

// Translation a synthetic scene into a valid SfM_Data scene.
// => A synthetic scene is used:
//    some random noise is added on observed structure data points
//    a tiny rotation to ground truth is added to the true rotation (in order to test BA effectiveness)
SfM_Data getInputScene
(
  const NViewDataSet & d,
  const nViewDatasetConfigurator & config,
  EINTRINSIC eintrinsic,
  const bool b_use_gcp
)
{
  // Translate the input dataset to a SfM_Data scene
  SfM_Data sfm_data;

  // 1. Views
  // 2. Poses
  // 3. Intrinsic data (shared, so only one camera intrinsic is defined)
  // 4. Landmarks
  // 5. GCP (optional)

  const int nviews = d._C.size();
  const int npoints = d._X.cols();

  // 1. Views
  for (int i = 0; i < nviews; ++i)
  {
    const IndexT id_view = i, id_pose = i, id_intrinsic = 0; //(shared intrinsics)
    sfm_data.views[i] = std::make_shared<View>("", id_view, id_intrinsic, id_pose, config._cx *2, config._cy *2);
  }

  // Add a rotation to the GT (in order to make BA do some work)
  const Mat3 rot = RotationAroundX(D2R(6));

  // 2. Poses
  for (int i = 0; i < nviews; ++i)
  {
    Pose3 pose(rot * d._R[i], d._C[i]);
    sfm_data.poses[i] = pose;
  }

  // 3. Intrinsic data (shared, so only one camera intrinsic is defined)
  {
    const unsigned int w = config._cx *2;
    const unsigned int h = config._cy *2;
    switch (eintrinsic)
    {
      case PINHOLE_CAMERA:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic>
          (w, h, config._fx, config._cx, config._cy);
      break;
      case PINHOLE_CAMERA_RADIAL1:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Radial_K1>
          (w, h, config._fx, config._cx, config._cy, 0.0);
      break;
      case PINHOLE_CAMERA_RADIAL3:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Radial_K3>
          (w, h, config._fx, config._cx, config._cy, 0., 0., 0.);
      break;
      case PINHOLE_CAMERA_BROWN:
        sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Brown_T2>
          (w, h, config._fx, config._cx, config._cy, 0., 0., 0., 0., 0.);
      break;
      case PINHOLE_CAMERA_FISHEYE:
      sfm_data.intrinsics[0] = std::make_shared<Pinhole_Intrinsic_Fisheye>
          (w, h, config._fx, config._cx, config._cy, 0., 0., 0., 0.);
      break;
      default:
        std::cout << "Not yet supported" << std::endl;
    }
  }

  // 4. Landmarks
  // Collect image observation of the landmarks X in each frame.
  // => add some random noise to each (x,y) observation
  std::default_random_engine random_generator;
  std::normal_distribution<double> distribution(0, 0.1);
  for (int i = 0; i < npoints; ++i)
  {
    // Create a landmark for each 3D points
    Landmark landmark;
    landmark.X = d._X.col(i);
    for (int j = 0; j < nviews; ++j)
    {
      Vec2 pt = d._x[j].col(i);
      // Add some noise to image observations
      pt(0) += distribution(random_generator);
      pt(1) += distribution(random_generator);

      landmark.obs[j] = Observation(pt, i);
    }
    sfm_data.structure[i] = landmark;
  }

  // 5. GCP
  if (b_use_gcp)
  {
    if (npoints >= 4) // Use 4 GCP for this test
    {
      for (int i = 0; i < 4; ++i) // Select the 4 first point as GCP
      {
        // Collect observations of the landmarks X in each frame.
        Landmark landmark;
        landmark.X = d._X.col(i);
        for (int j = 0; j < nviews; ++j)
        {
          landmark.obs[j] = Observation(d._x[j].col(i), i);
        }
        sfm_data.control_points[i] = landmark;
      }
    }
    else
    {
      std::cerr << "Insufficient point count" << std::endl;
    }
  }
  return sfm_data;
}

/* ************************************************************************* */
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
/* ************************************************************************* */
<|endoftext|>


<commit_before>/*
 * ModSecurity, http://www.modsecurity.org/
 * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
 *
 * 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
 *
 * If any of the files related to licensing are missing or if you have any
 * other questions related to licensing please contact Trustwave Holdings, Inc.
 * directly using the email address security@modsecurity.org.
 *
 */

#include "operators/validate_byte_range.h"

#include <string>

#include "operators/operator.h"

namespace modsecurity {
namespace operators {

bool ValidateByteRange::getRange(const std::string &rangeRepresentation,
    std::string *error) {
    size_t pos = param.find_first_of("-");
    int start;
    int end;

    if (pos == std::string::npos) {
        try {
            start = std::stoi(rangeRepresentation);
        } catch(...) {
            error->assign("Not able to convert '" + rangeRepresentation +
                "' into a number");
            return false;
        }
        table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7)));
        return true;
    }

    try {
        start = std::stoi(std::string(rangeRepresentation, 0, pos));
    } catch (...) {
        error->assign("Not able to convert '" +
            std::string(rangeRepresentation, 0, pos) +
            "' into a number");
        return false;
    }

    try {
        end = std::stoi(std::string(rangeRepresentation, pos + 1,
            rangeRepresentation.length() - (pos + 1)));
    } catch (...) {
        error->assign("Not able to convert '" + std::string(rangeRepresentation,
            pos + 1, rangeRepresentation.length() - (pos + 1)) +
            "' into a number");
        return false;
    }

    if ((start < 0) || (start > 255)) {
        error->assign("Invalid range start value: " +
            std::to_string(start));
        return false;
    }
    if ((end < 0) || (end > 255)) {
       error->assign("Invalid range end value: " + std::to_string(end));
       return false;
    }
    if (start > end) {
       error->assign("Invalid range: " + std::to_string(start) + "-" +
           std::to_string(end));
       return false;
    }

    while (start <= end) {
        table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7)));
        start++;
    }

    return true;
}

bool ValidateByteRange::init(const std::string &file,
    std::string *error) {
    size_t pos = param.find_first_of(",");

    if (pos == std::string::npos) {
        getRange(param, error);
    }

    while (pos != std::string::npos) {
        size_t next_pos = param.find_first_of(",", pos + 1);
        if (next_pos == std::string::npos) {
            getRange(std::string(param, pos + 1, param.length() -
                (pos + 1)), error);
        } else {
            getRange(std::string(param, pos + 1, next_pos), error);
        }
        pos = next_pos;
    }

    return true;
}

bool ValidateByteRange::evaluate(Transaction *transaction,
    const std::string &input) {
    bool ret = true;

    size_t count = 0;
    for (int i = 0; i < input.length(); i++) {
        int x = input.at(i);
        if (!(table[x >> 3] & (1 << (x & 0x7)))) {
            // debug(9, "Value " + std::to_string(x) + " in " +
            //     input + " ouside range: " + param);
            count++;
        }
    }

    ret = (count != 0);

    // debug(9, "Found %d byte(s) in %s outside range: %s.",
    //     count, var->name, rule->op_param);

    if (negation) {
        return !ret;
    }

    return ret;
}

}  // namespace operators
}  // namespace modsecurity
<commit_msg>Fix @validateByteRange initialization<commit_after>/*
 * ModSecurity, http://www.modsecurity.org/
 * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/)
 *
 * 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
 *
 * If any of the files related to licensing are missing or if you have any
 * other questions related to licensing please contact Trustwave Holdings, Inc.
 * directly using the email address security@modsecurity.org.
 *
 */

#include "operators/validate_byte_range.h"

#include <string>

#include "operators/operator.h"

namespace modsecurity {
namespace operators {

bool ValidateByteRange::getRange(const std::string &rangeRepresentation,
    std::string *error) {
    size_t pos = rangeRepresentation.find_first_of("-");
    int start;
    int end;

    if (pos == std::string::npos) {
        try {
            start = std::stoi(rangeRepresentation);
        } catch(...) {
            error->assign("Not able to convert '" + rangeRepresentation +
                "' into a number");
            return false;
        }
        table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7)));
        return true;
    }

    try {
        start = std::stoi(std::string(rangeRepresentation, 0, pos));
    } catch (...) {
        error->assign("Not able to convert '" +
            std::string(rangeRepresentation, 0, pos) +
            "' into a number");
        return false;
    }

    try {
        end = std::stoi(std::string(rangeRepresentation, pos + 1,
            rangeRepresentation.length() - (pos + 1)));
    } catch (...) {
        error->assign("Not able to convert '" + std::string(rangeRepresentation,
            pos + 1, rangeRepresentation.length() - (pos + 1)) +
            "' into a number");
        return false;
    }

    if ((start < 0) || (start > 255)) {
        error->assign("Invalid range start value: " +
            std::to_string(start));
        return false;
    }
    if ((end < 0) || (end > 255)) {
       error->assign("Invalid range end value: " + std::to_string(end));
       return false;
    }
    if (start > end) {
       error->assign("Invalid range: " + std::to_string(start) + "-" +
           std::to_string(end));
       return false;
    }

    while (start <= end) {
        table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7)));
        start++;
    }

    return true;
}

bool ValidateByteRange::init(const std::string &file,
    std::string *error) {
    size_t pos = param.find_first_of(",");

    if (pos == std::string::npos) {
        getRange(param, error);
    } else {
        getRange(std::string(param, 0, pos), error);
    }

    while (pos != std::string::npos) {
        size_t next_pos = param.find_first_of(",", pos + 1);

        if (next_pos == std::string::npos) {
            getRange(std::string(param, pos + 1, param.length() -
                (pos + 1)), error);
        } else {
            getRange(std::string(param, pos + 1, next_pos), error);
        }
        pos = next_pos;
    }

    return true;
}

bool ValidateByteRange::evaluate(Transaction *transaction,
    const std::string &input) {
    bool ret = true;

    size_t count = 0;
    for (int i = 0; i < input.length(); i++) {
        int x = input.at(i);
        if (!(table[x >> 3] & (1 << (x & 0x7)))) {
            // debug(9, "Value " + std::to_string(x) + " in " +
            //     input + " ouside range: " + param);
            count++;
        }
    }

    ret = (count != 0);

    // debug(9, "Found %d byte(s) in %s outside range: %s.",
    //     count, var->name, rule->op_param);

    if (negation) {
        return !ret;
    }

    return ret;
}

}  // namespace operators
}  // namespace modsecurity
<|endoftext|>