<commit_before>#include <fstream>
#include <string>
#include <iostream>
#include <tuple>
#include <functional>
#include <vector>
#include <chrono>
#include <ctime>

using namespace std;
using namespace std::chrono;

class SparseLevenshteinAutomaton {
  private:
	string word;
	int n;
  public:
    void set_values (string auto_word, int num_mis);
    tuple<vector<int>, vector<int>> start();
    tuple<vector<int>, vector<int>> step(tuple<vector<int>, vector<int>> &previous, char c);
    bool is_match(tuple<vector<int>, vector<int>>& previous);
};

void SparseLevenshteinAutomaton::set_values (string auto_word, int num_mis) {
      word = auto_word;
      n = num_mis;
    }

tuple<vector<int>, vector<int>> SparseLevenshteinAutomaton::start () {
	vector<int> range_max_edits;
	for (int i=0; i <= n; i++) {
		range_max_edits.push_back(i);
	}
	return make_tuple(range_max_edits, range_max_edits);
}

tuple<vector<int>, vector<int>> SparseLevenshteinAutomaton::step(tuple<vector<int>, vector<int>>& previous, char c) {
	vector<int> new_indices;
	vector<int> new_values;
	if (get<0>(previous).size() and get<0>(previous).at(0) == 0 and get<1>(previous).at(0) < n) {
		new_indices = {0};
		new_values = {get<1>(previous).at(0) + 1};
	}
	else {
		new_indices = {};
		new_values = {};
	}
	for (int i = 0; i < get<0>(previous).size(); i++) {
	}
	for (int i = 0; i < get<0>(previous).size(); i++) {
		if (get<0>(previous).at(i) == word.length()) {
			break;
		}
		int cost;
		if (word[get<0>(previous).at(i)] == c)
		{
			cost = 0;
		}
		else {
			cost = 1;
		}
		int val = get<1>(previous).at(i) + cost;
		if (new_indices.size() and new_indices.at(new_indices.size() - 1) == get<0>(previous).at(i)) {
			val = min(val, (new_values.at(new_values.size() - 1) + 1));
		}
		if ((i + 1) < get<0>(previous).size() and get<0>(previous).at(i + 1) == get<0>(previous).at(i) + 1) {
			val = min(val, (get<1>(previous).at(i + 1) + 1));
		}
		if (val <= n) {
			new_indices.push_back(get<0>(previous).at(i) + 1);
			new_values.push_back(val);
		}
	}
	return make_tuple(new_indices, new_values);

}

bool SparseLevenshteinAutomaton::is_match(tuple<vector<int>, vector<int>>& previous) {
  return (get<0>(previous).size() and get<0>(previous).at(get<0>(previous).size() - 1) == word.length());
}

void benchmark(size_t mistakes,
				const vector<string> &first_file_seq,
				 const vector<string> &second_file_seq,
				 size_t first_file_size,
				 size_t second_file_size, ofstream &outfile)
{
	std::chrono::milliseconds tp1 = std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch());

	for (int k = 0; k < first_file_size; k++)  {
		string auto_word = first_file_seq[k];
		SparseLevenshteinAutomaton sparse;
		sparse.set_values (auto_word, mistakes);
		tuple<vector<int>, vector<int>> s_sparse1 = sparse.start();
		for (int j = 0; j < second_file_size; j++)  {
			tuple<vector<int>, vector<int>> s_sparse = s_sparse1;
			 for (int i = 0; i < second_file_seq[j].length(); i++) {
				 s_sparse = sparse.step(s_sparse, second_file_seq[j][i]);
				 if ((i == second_file_seq[j].length() - 1) && sparse.is_match(s_sparse)) {
						 // outfile << word_check << " " << auto_word << "\n";
					 	outfile << (int) i << " " << (int) j << "\n";
				 }
			 }
		}
	  }

	std::chrono::milliseconds tp2 = std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch());
	auto diff = tp2.count() - tp1.count();

	cout << "benchmark (" << (size_t) first_file_size << " : " << second_file_size << ") - " << diff << " milsec" << endl;
}

int main (int argc, char** argv) {
  string line1, line2;
  char* file1 = argv[1];
  char* file2 = argv[2];
  int mistakes = atoi(argv[3]);
  string outfilename = argv[4];
  ifstream myfile2 (file2);
  vector<string> first_file_seq;
  vector<string> second_file_seq;
  if (myfile2.is_open())
    {
      while ( getline (myfile2,line2) )
      {
        second_file_seq.push_back(line2);
      }
      myfile2.close();
    }
  ifstream myfile1 (file1);
  ofstream outfile;
  outfile.open(outfilename,  std::ios_base::app);
  if (myfile1.is_open())
      {
        while ( getline (myfile1,line1) )
        {
        	first_file_seq.push_back(line1);
        }
        myfile1.close();
      }
  benchmark(mistakes, first_file_seq, second_file_seq, 100, 100, outfile);
  benchmark(mistakes, first_file_seq, second_file_seq, 500, 500, outfile);
  // benchmark(mistakes, first_file_seq, second_file_seq, 1000, 1000, outfile);
  // benchmark(mistakes, first_file_seq, second_file_seq, 3000, 3000, outfile);
  // benchmark(mistakes, first_file_seq, second_file_seq, 5000, 5000, outfile);
  outfile.close();
  return 0;
}<commit_msg>few minor changes<commit_after>#include <fstream>
#include <string>
#include <iostream>
#include <tuple>
#include <functional>
#include <vector>
#include <chrono>
#include <ctime>

using namespace std;
using namespace std::chrono;

class SparseLevenshteinAutomaton {
  private:
	string word;
	int n;
  public:
    void set_values (string auto_word, int num_mis);
    tuple<vector<int>, vector<int>> start();
    tuple<vector<int>, vector<int>> step(tuple<vector<int>, vector<int>> &previous, char c);
    bool is_match(tuple<vector<int>, vector<int>>& previous);
};

void SparseLevenshteinAutomaton::set_values (string auto_word, int num_mis) {
      word = auto_word;
      n = num_mis;
    }

tuple<vector<int>, vector<int>> SparseLevenshteinAutomaton::start () {
	vector<int> range_max_edits;
	for (int i=0; i <= n; i++) {
		range_max_edits.push_back(i);
	}
	return make_tuple(range_max_edits, range_max_edits);
}

tuple<vector<int>, vector<int>> SparseLevenshteinAutomaton::step(tuple<vector<int>, vector<int>>& previous, char c) {
	vector<int> new_indices;
	vector<int> new_values;
	if (get<0>(previous).size() and get<0>(previous).at(0) == 0 and get<1>(previous).at(0) < n) {
		new_indices = {0};
		new_values = {get<1>(previous).at(0) + 1};
	}
	else {
		new_indices = {};
		new_values = {};
	}
	for (int i = 0; i < get<0>(previous).size(); i++) {
	}
	for (int i = 0; i < get<0>(previous).size(); i++) {
		if (get<0>(previous).at(i) == word.length()) {
			break;
		}
		int cost;
		if (word[get<0>(previous).at(i)] == c)
		{
			cost = 0;
		}
		else {
			cost = 1;
		}
		int val = get<1>(previous).at(i) + cost;
		if (new_indices.size() and new_indices.at(new_indices.size() - 1) == get<0>(previous).at(i)) {
			val = min(val, (new_values.at(new_values.size() - 1) + 1));
		}
		if ((i + 1) < get<0>(previous).size() and get<0>(previous).at(i + 1) == get<0>(previous).at(i) + 1) {
			val = min(val, (get<1>(previous).at(i + 1) + 1));
		}
		if (val <= n) {
			new_indices.push_back(get<0>(previous).at(i) + 1);
			new_values.push_back(val);
		}
	}
	return make_tuple(new_indices, new_values);

}

bool SparseLevenshteinAutomaton::is_match(tuple<vector<int>, vector<int>>& previous) {
  return (get<0>(previous).size() and get<0>(previous).at(get<0>(previous).size() - 1) == word.length());
}

void benchmark(size_t mistakes,
				const vector<string> &first_file_seq,
				 const vector<string> &second_file_seq,
				 size_t first_file_size,
				 size_t second_file_size, ofstream &outfile)
{
	std::chrono::milliseconds tp1 = std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch());

	SparseLevenshteinAutomaton sparse;
	for (int k = 0; k < first_file_size; k++)  {
		// string auto_word = first_file_seq[k];
		// SparseLevenshteinAutomaton sparse;
		sparse.set_values (first_file_seq[k], mistakes);
		tuple<vector<int>, vector<int>> s_sparse1 = sparse.start();
		for (int j = 0; j < second_file_size; j++)  {
			tuple<vector<int>, vector<int>> s_sparse = s_sparse1;
			 for (int i = 0; i < second_file_seq[j].length(); i++) {
				 s_sparse = sparse.step(s_sparse, second_file_seq[j][i]);
				 if ((i == second_file_seq[j].length() - 1) && sparse.is_match(s_sparse)) {
						 // outfile << word_check << " " << auto_word << "\n";
					 	outfile << (int) (i+1) << " " << (int) (j+1) << "\n";
					 	// cout << (size_t) get<0>(s_sparse).size() << ":" << (size_t) get<1>(s_sparse).size() << endl;
				 }
			 }
		}
	  }

	std::chrono::milliseconds tp2 = std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch());
	auto diff = tp2.count() - tp1.count();

	cout << "benchmark (" << (size_t) first_file_size << " : " << second_file_size << ") - " << diff << " milsec" << endl;
}

int main (int argc, char** argv) {
  string line1, line2;
  char* file1 = argv[1];
  char* file2 = argv[2];
  int mistakes = atoi(argv[3]);
  string outfilename = argv[4];
  ifstream myfile2 (file2);
  vector<string> first_file_seq;
  vector<string> second_file_seq;
  if (myfile2.is_open())
    {
      while ( getline (myfile2,line2) )
      {
        second_file_seq.push_back(line2);
      }
      myfile2.close();
    }
  ifstream myfile1 (file1);
  ofstream outfile;
  outfile.open(outfilename,  std::ios_base::app);
  if (myfile1.is_open())
      {
        while ( getline (myfile1,line1) )
        {
        	first_file_seq.push_back(line1);
        }
        myfile1.close();
      }
  benchmark(mistakes, first_file_seq, second_file_seq, 100, 100, outfile);
  benchmark(mistakes, first_file_seq, second_file_seq, 500, 500, outfile);
  // benchmark(mistakes, first_file_seq, second_file_seq, 1000, 1000, outfile);
  // benchmark(mistakes, first_file_seq, second_file_seq, 3000, 3000, outfile);
  // benchmark(mistakes, first_file_seq, second_file_seq, 5000, 5000, outfile);
  outfile.close();
  return 0;
}<|endoftext|>


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

#include "Crypto.h"
#include "Gost.h"
#include "key.h"

#include <openssl/ecdsa.h>
#include <openssl/rand.h>
#include <openssl/obj_mac.h>

// anonymous namespace with local implementation code (OpenSSL interaction)
namespace {

// Generate a private key from just the secret parameter
int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
{
    int ok = 0;
    BN_CTX *ctx = NULL;
    EC_POINT *pub_key = NULL;

    if (!eckey) return 0;

    const EC_GROUP *group = EC_KEY_get0_group(eckey);

    if ((ctx = BN_CTX_new()) == NULL)
        goto err;

    pub_key = EC_POINT_new(group);

    if (pub_key == NULL)
        goto err;

    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
        goto err;

    EC_KEY_set_private_key(eckey,priv_key);
    EC_KEY_set_public_key(eckey,pub_key);

    ok = 1;

err:

    if (pub_key)
        EC_POINT_free(pub_key);
    if (ctx != NULL)
        BN_CTX_free(ctx);

    return(ok);
}

// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
// recid selects which key is recovered
// if check is non-zero, additional checks are performed
static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
{
    if (!eckey) return 0;
	BIGNUM * d = BN_bin2bn (msg, msglen, nullptr);
	const auto& curve = i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA);
    const BIGNUM * r, * s;
	ECDSA_SIG_get0 (ecsig, &r, &s);
	EC_POINT * pub = curve->RecoverPublicKey (d, r, s, recid % 2);
	BN_free (d);
	if (!pub) return 0;
	EC_KEY_set_public_key(eckey, pub);
	EC_POINT_free (pub);
	return 1;
}

// RAII Wrapper around OpenSSL's EC_KEY
class CECKey {
private:
    EC_KEY *pkey;

public:
    CECKey()
	{
		pkey = EC_KEY_new ();
		// GOST 34.10-2012
		auto ret = EC_KEY_set_group(pkey, i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->GetGroup ());
		assert (ret == 1);
    }

    ~CECKey() {
        EC_KEY_free(pkey);
    }

    void GetSecretBytes(unsigned char vch[32]) const {
        const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
        assert(bn);
        int nBytes = BN_num_bytes(bn);
        int n=BN_bn2bin(bn,&vch[32 - nBytes]);
        assert(n == nBytes);
        memset(vch, 0, 32 - nBytes);
    }

    void SetSecretBytes(const unsigned char vch[32]) {
        BIGNUM bn;
        BN_init(&bn);
        assert(BN_bin2bn(vch, 32, &bn));
        assert(EC_KEY_regenerate_key(pkey, &bn));
        BN_clear_free(&bn);
    }

    void GetPrivKey(CPrivKey &privkey, bool fCompressed) {
        EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
        int nSize = i2d_ECPrivateKey(pkey, NULL);
        assert(nSize);
        privkey.resize(nSize);
        unsigned char* pbegin = &privkey[0];
        int nSize2 = i2d_ECPrivateKey(pkey, &pbegin);
        assert(nSize == nSize2);
    }

    bool SetPrivKey(const CPrivKey &privkey) {
        const unsigned char* pbegin = &privkey[0];
        if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) {
            // d2i_ECPrivateKey returns true if parsing succeeds.
            // This doesn't necessarily mean the key is valid.
            if (EC_KEY_check_key(pkey))
                return true;
        }
        return false;
    }

    void GetPubKey(CPubKey &pubkey, bool fCompressed) {
        EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
        int nSize = i2o_ECPublicKey(pkey, NULL);
        assert(nSize);
        assert(nSize <= 65);
        unsigned char c[65];
        unsigned char *pbegin = c;
        int nSize2 = i2o_ECPublicKey(pkey, &pbegin);
        assert(nSize == nSize2);
        pubkey.Set(&c[0], &c[nSize]);
    }

    bool SetPubKey(const CPubKey &pubkey) {
        const unsigned char* pbegin = pubkey.begin();
        return o2i_ECPublicKey(&pkey, &pbegin, pubkey.size());
    }

    bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig)
	{
		const BIGNUM * priv = EC_KEY_get0_private_key(pkey);
		BIGNUM * d = BN_bin2bn (hash.begin (), 32, nullptr);
        BIGNUM * r = BN_new (), * s = BN_new ();
		i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->Sign (priv, d, r, s);
        ECDSA_SIG *sig = ECDSA_SIG_new ();
        ECDSA_SIG_set0 (sig, r, s);
		// encode signature is in DER format
		auto nSize = ECDSA_size (pkey); // max size
		vchSig.resize(nSize);
		auto p = &vchSig[0];
		nSize = i2d_ECDSA_SIG (sig, &p);
		vchSig.resize(nSize); // acutal size
		BN_free (d);
		ECDSA_SIG_free(sig);
        return true;
    }

    bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig)
	{
		// decode from DER
		ECDSA_SIG *sig = nullptr;
		auto p = &vchSig[0];
		d2i_ECDSA_SIG (&sig, &p, vchSig.size());
		const EC_POINT * pub = EC_KEY_get0_public_key(pkey);
		BIGNUM * d = BN_bin2bn (hash.begin (), 32, nullptr);
        const BIGNUM * r, * s;
		ECDSA_SIG_get0 (sig, &r, &s);
		bool ret = i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->Verify (pub, d, r, s);
		BN_free (d);
		ECDSA_SIG_free(sig);
		return ret;
    }

    bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec)
	{
        bool fOk = false;
        ECDSA_SIG *sig = ECDSA_SIG_new ();
		const BIGNUM * priv = EC_KEY_get0_private_key(pkey);
		BIGNUM * d = BN_bin2bn (hash.begin (), 32, nullptr);
        BIGNUM * r = BN_new (), * s = BN_new ();
		i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->Sign (priv, d, r, s);
        ECDSA_SIG_set0 (sig, r, s);
		BN_free (d);
        if (sig==NULL)
            return false;
        memset(p64, 0, 64);
        int nBitsR = BN_num_bits(r);
        int nBitsS = BN_num_bits(s);
        if (nBitsR <= 256 && nBitsS <= 256) {
            CPubKey pubkey;
            GetPubKey(pubkey, true);
            for (int i=0; i<2; i++) {
                CECKey keyRec;
                if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) {
                    CPubKey pubkeyRec;
                    keyRec.GetPubKey(pubkeyRec, true);
                    if (pubkeyRec == pubkey) {
                        rec = i;
                        fOk = true;
                        break;
                    }
                }
            }
            assert(fOk);
            BN_bn2bin(r,&p64[32-(nBitsR+7)/8]);
            BN_bn2bin(s,&p64[64-(nBitsS+7)/8]);
        }
        ECDSA_SIG_free(sig);
        return fOk;
    }

    // reconstruct public key from a compact signature
    // This is only slightly more CPU intensive than just verifying it.
    // If this function succeeds, the recovered public key is guaranteed to be valid
    // (the signature is a valid signature of the given data for that key)
    bool Recover(const uint256 &hash, const unsigned char *p64, int rec)
    {
        if (rec<0 || rec>=3)
            return false;
        ECDSA_SIG *sig = ECDSA_SIG_new();
        auto r = BN_bin2bn(&p64[0],  32, NULL);
        auto s = BN_bin2bn(&p64[32], 32, NULL);
        ECDSA_SIG_set0 (sig, r, s);
        bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1;
        ECDSA_SIG_free(sig);
        return ret;
    }
};

}; // end of anonymous namespace

bool CKey::Check(const unsigned char *vch) {
    // Do not convert to OpenSSL's data structures for range-checking keys,
    // it's easy enough to do directly.
    static const unsigned char vchMax[32] = {
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
        0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
        0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
    };
    bool fIsZero = true;
    for (int i=0; i<32 && fIsZero; i++)
        if (vch[i] != 0)
            fIsZero = false;
    if (fIsZero)
        return false;
    for (int i=0; i<32; i++) {
        if (vch[i] < vchMax[i])
            return true;
        if (vch[i] > vchMax[i])
            return false;
    }
    return true;
}

void CKey::MakeNewKey(bool fCompressedIn) {
    do {
        RAND_bytes(vch, sizeof(vch));
    } while (!Check(vch));
    fValid = true;
    fCompressed = fCompressedIn;
}

bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
    CECKey key;
    if (!key.SetPrivKey(privkey))
        return false;
    key.GetSecretBytes(vch);
    fCompressed = fCompressedIn;
    fValid = true;
    return true;
}

CPrivKey CKey::GetPrivKey() const {
    assert(fValid);
    CECKey key;
    key.SetSecretBytes(vch);
    CPrivKey privkey;
    key.GetPrivKey(privkey, fCompressed);
    return privkey;
}

CPubKey CKey::GetPubKey() const {
    assert(fValid);
    CECKey key;
    key.SetSecretBytes(vch);
    CPubKey pubkey;
    key.GetPubKey(pubkey, fCompressed);
    return pubkey;
}

bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
    if (!fValid)
        return false;
    CECKey key;
    key.SetSecretBytes(vch);
    return key.Sign(hash, vchSig);
}

bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
    if (!fValid)
        return false;
    CECKey key;
    key.SetSecretBytes(vch);
    vchSig.resize(65);
    int rec = -1;
    if (!key.SignCompact(hash, &vchSig[1], rec))
        return false;
    assert(rec != -1);
    vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
    return true;
}

bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
    if (!IsValid())
        return false;
    CECKey key;
    if (!key.SetPubKey(*this))
        return false;
    if (!key.Verify(hash, vchSig))
        return false;
    return true;
}

bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
    if (vchSig.size() != 65)
        return false;
    CECKey key;
    if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4))
        return false;
    key.GetPubKey(*this, (vchSig[0] - 27) & 4);
    return true;
}

bool CPubKey::VerifyCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
    if (!IsValid())
        return false;
    if (vchSig.size() != 65)
        return false;
    CECKey key;
    if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4))
        return false;
    CPubKey pubkeyRec;
    key.GetPubKey(pubkeyRec, IsCompressed());
    if (*this != pubkeyRec)
        return false;
    return true;
}

bool CPubKey::IsFullyValid() const {
    if (!IsValid())
        return false;
    CECKey key;
    if (!key.SetPubKey(*this))
        return false;
    return true;
}

bool CPubKey::Decompress() {
    if (!IsValid())
        return false;
    CECKey key;
    if (!key.SetPubKey(*this))
        return false;
    key.GetPubKey(*this, false);
    return true;
}
<commit_msg>openssl 1.1 support<commit_after>// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "Crypto.h"
#include "Gost.h"
#include "key.h"

#include <openssl/ecdsa.h>
#include <openssl/rand.h>
#include <openssl/obj_mac.h>

// anonymous namespace with local implementation code (OpenSSL interaction)
namespace {

// Generate a private key from just the secret parameter
int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
{
    int ok = 0;
    BN_CTX *ctx = NULL;
    EC_POINT *pub_key = NULL;

    if (!eckey) return 0;

    const EC_GROUP *group = EC_KEY_get0_group(eckey);

    if ((ctx = BN_CTX_new()) == NULL)
        goto err;

    pub_key = EC_POINT_new(group);

    if (pub_key == NULL)
        goto err;

    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
        goto err;

    EC_KEY_set_private_key(eckey,priv_key);
    EC_KEY_set_public_key(eckey,pub_key);

    ok = 1;

err:

    if (pub_key)
        EC_POINT_free(pub_key);
    if (ctx != NULL)
        BN_CTX_free(ctx);

    return(ok);
}

// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
// recid selects which key is recovered
// if check is non-zero, additional checks are performed
static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
{
    if (!eckey) return 0;
	BIGNUM * d = BN_bin2bn (msg, msglen, nullptr);
	const auto& curve = i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA);
    const BIGNUM * r, * s;
	ECDSA_SIG_get0 (ecsig, &r, &s);
	EC_POINT * pub = curve->RecoverPublicKey (d, r, s, recid % 2);
	BN_free (d);
	if (!pub) return 0;
	EC_KEY_set_public_key(eckey, pub);
	EC_POINT_free (pub);
	return 1;
}

// RAII Wrapper around OpenSSL's EC_KEY
class CECKey {
private:
    EC_KEY *pkey;

public:
    CECKey()
	{
		pkey = EC_KEY_new ();
		// GOST 34.10-2012
		auto ret = EC_KEY_set_group(pkey, i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->GetGroup ());
		assert (ret == 1);
    }

    ~CECKey() {
        EC_KEY_free(pkey);
    }

    void GetSecretBytes(unsigned char vch[32]) const {
        const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
        assert(bn);
        int nBytes = BN_num_bytes(bn);
        int n=BN_bn2bin(bn,&vch[32 - nBytes]);
        assert(n == nBytes);
        memset(vch, 0, 32 - nBytes);
    }

    void SetSecretBytes(const unsigned char vch[32]) {
        BIGNUM * bn = BN_new ();
        assert(BN_bin2bn(vch, 32, bn));
        assert(EC_KEY_regenerate_key(pkey, bn));
        BN_clear_free(bn);
    }

    void GetPrivKey(CPrivKey &privkey, bool fCompressed) {
        EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
        int nSize = i2d_ECPrivateKey(pkey, NULL);
        assert(nSize);
        privkey.resize(nSize);
        unsigned char* pbegin = &privkey[0];
        int nSize2 = i2d_ECPrivateKey(pkey, &pbegin);
        assert(nSize == nSize2);
    }

    bool SetPrivKey(const CPrivKey &privkey) {
        const unsigned char* pbegin = &privkey[0];
        if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) {
            // d2i_ECPrivateKey returns true if parsing succeeds.
            // This doesn't necessarily mean the key is valid.
            if (EC_KEY_check_key(pkey))
                return true;
        }
        return false;
    }

    void GetPubKey(CPubKey &pubkey, bool fCompressed) {
        EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
        int nSize = i2o_ECPublicKey(pkey, NULL);
        assert(nSize);
        assert(nSize <= 65);
        unsigned char c[65];
        unsigned char *pbegin = c;
        int nSize2 = i2o_ECPublicKey(pkey, &pbegin);
        assert(nSize == nSize2);
        pubkey.Set(&c[0], &c[nSize]);
    }

    bool SetPubKey(const CPubKey &pubkey) {
        const unsigned char* pbegin = pubkey.begin();
        return o2i_ECPublicKey(&pkey, &pbegin, pubkey.size());
    }

    bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig)
	{
		const BIGNUM * priv = EC_KEY_get0_private_key(pkey);
		BIGNUM * d = BN_bin2bn (hash.begin (), 32, nullptr);
        BIGNUM * r = BN_new (), * s = BN_new ();
		i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->Sign (priv, d, r, s);
        ECDSA_SIG *sig = ECDSA_SIG_new ();
        ECDSA_SIG_set0 (sig, r, s);
		// encode signature is in DER format
		auto nSize = ECDSA_size (pkey); // max size
		vchSig.resize(nSize);
		auto p = &vchSig[0];
		nSize = i2d_ECDSA_SIG (sig, &p);
		vchSig.resize(nSize); // acutal size
		BN_free (d);
		ECDSA_SIG_free(sig);
        return true;
    }

    bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig)
	{
		// decode from DER
		ECDSA_SIG *sig = nullptr;
		auto p = &vchSig[0];
		d2i_ECDSA_SIG (&sig, &p, vchSig.size());
		const EC_POINT * pub = EC_KEY_get0_public_key(pkey);
		BIGNUM * d = BN_bin2bn (hash.begin (), 32, nullptr);
        const BIGNUM * r, * s;
		ECDSA_SIG_get0 (sig, &r, &s);
		bool ret = i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->Verify (pub, d, r, s);
		BN_free (d);
		ECDSA_SIG_free(sig);
		return ret;
    }

    bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec)
	{
        bool fOk = false;
        ECDSA_SIG *sig = ECDSA_SIG_new ();
		const BIGNUM * priv = EC_KEY_get0_private_key(pkey);
		BIGNUM * d = BN_bin2bn (hash.begin (), 32, nullptr);
        BIGNUM * r = BN_new (), * s = BN_new ();
		i2p::crypto::GetGOSTR3410Curve (i2p::crypto::eGOSTR3410CryptoProA)->Sign (priv, d, r, s);
        ECDSA_SIG_set0 (sig, r, s);
		BN_free (d);
        if (sig==NULL)
            return false;
        memset(p64, 0, 64);
        int nBitsR = BN_num_bits(r);
        int nBitsS = BN_num_bits(s);
        if (nBitsR <= 256 && nBitsS <= 256) {
            CPubKey pubkey;
            GetPubKey(pubkey, true);
            for (int i=0; i<2; i++) {
                CECKey keyRec;
                if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) {
                    CPubKey pubkeyRec;
                    keyRec.GetPubKey(pubkeyRec, true);
                    if (pubkeyRec == pubkey) {
                        rec = i;
                        fOk = true;
                        break;
                    }
                }
            }
            assert(fOk);
            BN_bn2bin(r,&p64[32-(nBitsR+7)/8]);
            BN_bn2bin(s,&p64[64-(nBitsS+7)/8]);
        }
        ECDSA_SIG_free(sig);
        return fOk;
    }

    // reconstruct public key from a compact signature
    // This is only slightly more CPU intensive than just verifying it.
    // If this function succeeds, the recovered public key is guaranteed to be valid
    // (the signature is a valid signature of the given data for that key)
    bool Recover(const uint256 &hash, const unsigned char *p64, int rec)
    {
        if (rec<0 || rec>=3)
            return false;
        ECDSA_SIG *sig = ECDSA_SIG_new();
        auto r = BN_bin2bn(&p64[0],  32, NULL);
        auto s = BN_bin2bn(&p64[32], 32, NULL);
        ECDSA_SIG_set0 (sig, r, s);
        bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1;
        ECDSA_SIG_free(sig);
        return ret;
    }
};

}; // end of anonymous namespace

bool CKey::Check(const unsigned char *vch) {
    // Do not convert to OpenSSL's data structures for range-checking keys,
    // it's easy enough to do directly.
    static const unsigned char vchMax[32] = {
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
        0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
        0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
    };
    bool fIsZero = true;
    for (int i=0; i<32 && fIsZero; i++)
        if (vch[i] != 0)
            fIsZero = false;
    if (fIsZero)
        return false;
    for (int i=0; i<32; i++) {
        if (vch[i] < vchMax[i])
            return true;
        if (vch[i] > vchMax[i])
            return false;
    }
    return true;
}

void CKey::MakeNewKey(bool fCompressedIn) {
    do {
        RAND_bytes(vch, sizeof(vch));
    } while (!Check(vch));
    fValid = true;
    fCompressed = fCompressedIn;
}

bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
    CECKey key;
    if (!key.SetPrivKey(privkey))
        return false;
    key.GetSecretBytes(vch);
    fCompressed = fCompressedIn;
    fValid = true;
    return true;
}

CPrivKey CKey::GetPrivKey() const {
    assert(fValid);
    CECKey key;
    key.SetSecretBytes(vch);
    CPrivKey privkey;
    key.GetPrivKey(privkey, fCompressed);
    return privkey;
}

CPubKey CKey::GetPubKey() const {
    assert(fValid);
    CECKey key;
    key.SetSecretBytes(vch);
    CPubKey pubkey;
    key.GetPubKey(pubkey, fCompressed);
    return pubkey;
}

bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
    if (!fValid)
        return false;
    CECKey key;
    key.SetSecretBytes(vch);
    return key.Sign(hash, vchSig);
}

bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
    if (!fValid)
        return false;
    CECKey key;
    key.SetSecretBytes(vch);
    vchSig.resize(65);
    int rec = -1;
    if (!key.SignCompact(hash, &vchSig[1], rec))
        return false;
    assert(rec != -1);
    vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
    return true;
}

bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
    if (!IsValid())
        return false;
    CECKey key;
    if (!key.SetPubKey(*this))
        return false;
    if (!key.Verify(hash, vchSig))
        return false;
    return true;
}

bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
    if (vchSig.size() != 65)
        return false;
    CECKey key;
    if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4))
        return false;
    key.GetPubKey(*this, (vchSig[0] - 27) & 4);
    return true;
}

bool CPubKey::VerifyCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
    if (!IsValid())
        return false;
    if (vchSig.size() != 65)
        return false;
    CECKey key;
    if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4))
        return false;
    CPubKey pubkeyRec;
    key.GetPubKey(pubkeyRec, IsCompressed());
    if (*this != pubkeyRec)
        return false;
    return true;
}

bool CPubKey::IsFullyValid() const {
    if (!IsValid())
        return false;
    CECKey key;
    if (!key.SetPubKey(*this))
        return false;
    return true;
}

bool CPubKey::Decompress() {
    if (!IsValid())
        return false;
    CECKey key;
    if (!key.SetPubKey(*this))
        return false;
    key.GetPubKey(*this, false);
    return true;
}
<|endoftext|>


<commit_before>#include "log.h"
#include <QDateTime>
#include <QDebug>
#include <QFileInfo>
#include <QList>
#include <QPair>
#include <QRegExp>
//#include <QtConcurrent>
#include <QScrollBar>
#include <QTextCodec>

LinePositionList allLinePos(const QByteArray &bts) {

  int count = 0;
  auto size = bts.size();
  for (int i = 0; i < size; ++i) {
    if (bts[i] == '\n') {
      count++;
    }
  }
  LinePositionList result(count);
  int start = 0;
  int index = 0;
  for (int i = 0; i < size; ++i) {
    if (bts[i] == '\n') {
      LinePosition lp;
      lp.first = start;
      lp.second = i;
      result[index++] = lp;
      start = i + 1;
    }
  }
  return result;
}

Log *Log::openFile(const QString &fname, const HighlightPatterns *global_highlight,
                   const QString &default_encoding, QObject *parent) {
  qDebug() << "openFile" << fname;
  QFileInfo fileInfo(fname);
  auto ll = new Log(fileInfo, fname, global_highlight, default_encoding, parent);
  return ll;
}

void Log::loadFile() {
  qDebug() << "loadFile " << m_fname;
  auto curDT = QDateTime::currentDateTimeUtc();
  QFile inputFile(m_fname);
  if (inputFile.open(QIODevice::ReadOnly)) {
    m_bts = std::move(inputFile.readAll());
    m_lines = allLinePos(m_bts);
    m_cache.resize(m_lines.size());
    inputFile.close();
  } else {
    throw std::logic_error("file not exists!");
  }
  qDebug() << "elapsed time:" << curDT.secsTo(QDateTime::currentDateTimeUtc());
  m_load_complete = true;
}

Log::Log(const QFileInfo &fileInfo, const QString &filename,
         const HighlightPatterns *global_highlight, const QString &default_encoding,
         QObject *parent)
    : QAbstractItemModel(parent) {
  m_default_encoding = default_encoding;
  m_fileInfo = fileInfo;
  m_lastModifed = m_fileInfo.lastModified();
  m_name = m_fileInfo.fileName();
  m_fname = filename;
  m_global_highlight = global_highlight;
  m_load_complete = false;
  m_codec = QTextCodec::codecForName(m_default_encoding.toStdString().c_str());
  if (m_codec == nullptr) {
    throw std::logic_error("m_codec==nullptr");
  }
  loadFile();
  auto idx = createIndex(-1, -1, nullptr);
  while (canFetchMore(idx)) {
    fetchMore(idx);
  }
  qDebug() << "loaded " << m_name << " lines:" << m_lines.size();
}

QString Log::name() const {
  return m_name;
}

QString Log::filename() const {
  return m_fname;
}

void Log::update() {
  qDebug() << "update " << m_name << "=>" << m_fname;
  std::lock_guard<std::mutex> lg(_locker);
  QFileInfo fileInfo(m_fname);

  //    if(fileInfo.lastModified()<=m_lastModifed){
  //        qDebug()<<"nothing to read: curDt:"<<fileInfo.lastModified()<<"
  //        myDt:"<<m_fileInfo.lastModified();
  //        return;
  //    }
  m_lastModifed = fileInfo.lastModified();

  qDebug() << "update " << m_fname;
  auto curDT = QDateTime::currentDateTimeUtc();
  QFile inputFile(m_fname);
  if (inputFile.open(QIODevice::ReadOnly)) {
    auto bts = inputFile.readAll();
    auto lines = allLinePos(bts);
    auto diff = lines.size() - m_lines.size();
    if (diff > 0) {
      auto old_size = m_lines.size();
      // m_load_complete=false;
      // this->beginResetModel();
      m_bts = std::move(bts);

      m_lines = std::move(lines);
      m_cache.clear();
      m_cache.resize(m_lines.size());

      m_load_complete = true;
      // this->endResetModel();

      emit countChanged(m_lines.size());
      emit linesChanged();

      if (_fltr != nullptr) {
        resetFilter(_fltr);
      } else {
        beginInsertRows(createIndex(0, 0, nullptr), old_size - 1, old_size + diff);
        //      for (auto &kv : local_res) {
        //        m_cache.insert(std::make_pair(kv.first, kv.second));
        //      }
        endInsertRows();
      }
      emit m_lv_object->scrollToBottom();
    }

    inputFile.close();
  } else {
    throw std::logic_error("file not exists!");
  }

  qDebug() << "update elapsed time:" << curDT.secsTo(QDateTime::currentDateTimeUtc());
}

std::shared_ptr<QString> Log::makeRawString(int row) const {
  qDebug() << "Log::makeRawString";
  auto line_pos = m_lines[row];
  int start = line_pos.first;
  int i = line_pos.second;

  int stringSize = int(i - start + 1);
  QByteArray localStr(stringSize, ' ');

  int insertPos = 0;
  for (int pos = start; pos < i; ++pos) {
    localStr[insertPos++] = m_bts[pos];
  }

  std::shared_ptr<QString> result =
      std::make_shared<QString>(m_codec->toUnicode(localStr));
  return result;
}

void Log::rawStringToValue(std::shared_ptr<QString> &rawString) const {
  if (m_global_highlight == nullptr) {
    throw std::logic_error("m_global_highlight==nullptr");
  }
  rawString->replace('<', "<");
  rawString->replace('>', ">");
  rawString->replace(' ', " "); // html eats white spaces
  for (auto it = m_global_highlight->begin(); it != m_global_highlight->end(); ++it) {
    heighlightStr(rawString.get(), *it);
  }
}

std::shared_ptr<QString> Log::makeString(int row) const {
  auto result = makeRawString(row);
  rawStringToValue(result);
  return result;
}

int Log::rowCount(const QModelIndex &parent) const {
  qDebug() << "Log::rowCount";
  std::lock_guard<std::mutex> lg(_locker);
  Q_UNUSED(parent);
  if (!m_load_complete) {
    return 0;
  }
  if (_fltr != nullptr) {
    return m_fltr_cache.size();
  }
  return m_lines.size();
  // return m_cache.size();
}

QVariant Log::data(const QModelIndex &index, int role) const {

  const QString emptyString = "EmptyString";
  if (!m_load_complete) {
    return emptyString;
  }

  if (index.row() < 0 || index.row() >= int(m_lines.size()))
    return QVariant();
  if (role == Qt::DisplayRole || role == Qt::EditRole) {
    qDebug() << "Log::data";
    std::lock_guard<std::mutex> lg(_locker);
    if (_fltr != nullptr) {
      return *m_fltr_cache[index.row()].Value;
    }
    if (m_cache[index.row()].Value != nullptr) {
      return *m_cache[index.row()].Value;
    } else {
      CachedString cs;
      cs.originValue = makeRawString(index.row());
      cs.index = index.row();
      rawStringToValue(cs.originValue);
      cs.Value = cs.originValue;
      m_cache[index.row()] = cs;
      return *cs.Value;
    }
  }
  return QVariant();
}

QString Log::plainText(const QModelIndex &index) const {
  if (index.row() < 0 || index.row() >= int(m_lines.size()))
    return QString("error");
  if (_fltr == nullptr) {
    return *makeRawString(index.row());
  } else {
    return *makeRawString(m_fltr_cache[index.row()].index);
  }
}

void Log::clearHightlight() {
  //    m_load_complete=false;
  //    this->beginResetModel();
  //    QtConcurrent::blockingMap(m_buffer,[](CachedString&cs){
  //        cs.Value=cs.originValue;
  //    });
  //    for(int k=0;k<m_buffer.size();++k){
  //        auto mi=this->createIndex(k,0);
  //        dataChanged(mi, mi);
  //    }
  //    m_load_complete=true;
  //    this->endResetModel();
}

bool Log::heighlightStr(QString *str, const HighlightPattern &pattern) {
  if (pattern.pattern.size() == 0) {
    return false;
  }
  bool result = false;

  QRegExp re(pattern.pattern);
  if (re.indexIn(*str) != -1) {
    auto ct = re.capturedTexts();
    for (auto &&captured_str : ct) {
      str->replace(re,
                   "" + captured_str +
                       "");
    }
    result = true;
  }

  return result;
}

void Log::updateHeighlights(QVector<CachedString>::iterator /*begin*/,
                            QVector<CachedString>::iterator /*end*/,
                            const QString &pattern) {
  if (pattern.size() == 0) {
    return;
  }
  auto curDT = QDateTime::currentDateTimeUtc();

  qDebug() << m_fname << "updateHeighlights elapsed time:"
           << curDT.secsTo(QDateTime::currentDateTimeUtc());
}

void Log::updateHeighlights(const QString & /*pattern*/) {
  // updateHeighlights(m_buffer.begin(), m_buffer.end(), pattern);
}

void Log::localHightlightPattern(const QString &pattern) {
  if (pattern.size() == 0) {
    return;
  }
  updateHeighlights(pattern);
}

void Log::setListVoxObject(QListView *object) {
  m_lv_object = object;
}

QPair<int, QString> Log::findFrom(const QString &pattern, int index,
                                  SearchDirection direction) {
  qDebug() << "Log::findFrom";
  std::lock_guard<std::mutex> lg(_locker);
  // TODO make thread safety. m_cache can be brokent, when timer is active
  if (index == m_cache.size() || index < 0) {
    return QPair<int, QString>(index, QString());
  }

  QRegExp re(pattern.toUpper());

  int i = direction == SearchDirection::Down ? index + 1 : index - 1;

  while (true) {
    QString str = plainText(createIndex(int(i), 0, nullptr)).toUpper();
    if (re.indexIn(str) != -1) {
      return QPair<int, QString>(i, str);
    }
    if (direction == SearchDirection::Down) {
      i++;
      if (i == m_cache.size()) {
        break;
      }
    } else {
      i--;
      if (i < 0) {
        break;
      }
    }
  }
  return QPair<int, QString>(index, "");
}

void Log::resetFilter(const Filter_Ptr &fltr) {
  qDebug() << "Log::resetFilter";
  std::lock_guard<std::mutex> lg(_locker);
  m_cache.resize(m_lines.size());
  setFilter_impl(fltr);
}

void Log::setFilter(const Filter_Ptr &fltr) {
  qDebug() << "Log::setFilter";
  std::lock_guard<std::mutex> lg(_locker);
  setFilter_impl(fltr);
}

void Log::setFilter_impl(const Filter_Ptr &fltr) {
  _fltr = fltr;
  int count = 0;
  m_fltr_cache.resize(m_lines.size());
  for (size_t i = 0; i < m_lines.size(); ++i) {
    auto qs = makeRawString(i);
    if (fltr->inFilter(*qs)) {
      rawStringToValue(qs);
      CachedString cs;
      cs.index = i;
      cs.originValue = qs;
      cs.Value = cs.originValue;
      m_fltr_cache[count] = cs;
      count++;
    }
  }

  beginResetModel();
  m_fltr_cache.resize(count);
  endResetModel();
}

void Log::clearFilter() {
  qDebug() << "Log::clearFilter";
  if (_fltr != nullptr) {
    beginResetModel();
    _fltr = nullptr;
    m_fltr_cache.resize(1);
    endResetModel();
  }
}
<commit_msg>rm debug output.<commit_after>#include "log.h"
#include <QDateTime>
#include <QDebug>
#include <QFileInfo>
#include <QList>
#include <QPair>
#include <QRegExp>
//#include <QtConcurrent>
#include <QScrollBar>
#include <QTextCodec>

LinePositionList allLinePos(const QByteArray &bts) {

  int count = 0;
  auto size = bts.size();
  for (int i = 0; i < size; ++i) {
    if (bts[i] == '\n') {
      count++;
    }
  }
  LinePositionList result(count);
  int start = 0;
  int index = 0;
  for (int i = 0; i < size; ++i) {
    if (bts[i] == '\n') {
      LinePosition lp;
      lp.first = start;
      lp.second = i;
      result[index++] = lp;
      start = i + 1;
    }
  }
  return result;
}

Log *Log::openFile(const QString &fname, const HighlightPatterns *global_highlight,
                   const QString &default_encoding, QObject *parent) {
  qDebug() << "openFile" << fname;
  QFileInfo fileInfo(fname);
  auto ll = new Log(fileInfo, fname, global_highlight, default_encoding, parent);
  return ll;
}

void Log::loadFile() {
  qDebug() << "loadFile " << m_fname;
  auto curDT = QDateTime::currentDateTimeUtc();
  QFile inputFile(m_fname);
  if (inputFile.open(QIODevice::ReadOnly)) {
    m_bts = std::move(inputFile.readAll());
    m_lines = allLinePos(m_bts);
    m_cache.resize(m_lines.size());
    inputFile.close();
  } else {
    throw std::logic_error("file not exists!");
  }
  qDebug() << "elapsed time:" << curDT.secsTo(QDateTime::currentDateTimeUtc());
  m_load_complete = true;
}

Log::Log(const QFileInfo &fileInfo, const QString &filename,
         const HighlightPatterns *global_highlight, const QString &default_encoding,
         QObject *parent)
    : QAbstractItemModel(parent) {
  m_default_encoding = default_encoding;
  m_fileInfo = fileInfo;
  m_lastModifed = m_fileInfo.lastModified();
  m_name = m_fileInfo.fileName();
  m_fname = filename;
  m_global_highlight = global_highlight;
  m_load_complete = false;
  m_codec = QTextCodec::codecForName(m_default_encoding.toStdString().c_str());
  if (m_codec == nullptr) {
    throw std::logic_error("m_codec==nullptr");
  }
  loadFile();
  auto idx = createIndex(-1, -1, nullptr);
  while (canFetchMore(idx)) {
    fetchMore(idx);
  }
  qDebug() << "loaded " << m_name << " lines:" << m_lines.size();
}

QString Log::name() const {
  return m_name;
}

QString Log::filename() const {
  return m_fname;
}

void Log::update() {
  qDebug() << "update " << m_name << "=>" << m_fname;
  std::lock_guard<std::mutex> lg(_locker);
  QFileInfo fileInfo(m_fname);

  //    if(fileInfo.lastModified()<=m_lastModifed){
  //        qDebug()<<"nothing to read: curDt:"<<fileInfo.lastModified()<<"
  //        myDt:"<<m_fileInfo.lastModified();
  //        return;
  //    }
  m_lastModifed = fileInfo.lastModified();

  qDebug() << "update " << m_fname;
  auto curDT = QDateTime::currentDateTimeUtc();
  QFile inputFile(m_fname);
  if (inputFile.open(QIODevice::ReadOnly)) {
    auto bts = inputFile.readAll();
    auto lines = allLinePos(bts);
    auto diff = lines.size() - m_lines.size();
    if (diff > 0) {
      auto old_size = m_lines.size();
      // m_load_complete=false;
      // this->beginResetModel();
      m_bts = std::move(bts);

      m_lines = std::move(lines);
      m_cache.clear();
      m_cache.resize(m_lines.size());

      m_load_complete = true;
      // this->endResetModel();

      emit countChanged(m_lines.size());
      emit linesChanged();

      if (_fltr != nullptr) {
        resetFilter(_fltr);
      } else {
        beginInsertRows(createIndex(0, 0, nullptr), old_size - 1, old_size + diff);
        //      for (auto &kv : local_res) {
        //        m_cache.insert(std::make_pair(kv.first, kv.second));
        //      }
        endInsertRows();
      }
      emit m_lv_object->scrollToBottom();
    }

    inputFile.close();
  } else {
    throw std::logic_error("file not exists!");
  }

  qDebug() << "update elapsed time:" << curDT.secsTo(QDateTime::currentDateTimeUtc());
}

std::shared_ptr<QString> Log::makeRawString(int row) const {
  //qDebug() << "Log::makeRawString";
  auto line_pos = m_lines[row];
  int start = line_pos.first;
  int i = line_pos.second;

  int stringSize = int(i - start + 1);
  QByteArray localStr(stringSize, ' ');

  int insertPos = 0;
  for (int pos = start; pos < i; ++pos) {
    localStr[insertPos++] = m_bts[pos];
  }

  std::shared_ptr<QString> result =
      std::make_shared<QString>(m_codec->toUnicode(localStr));
  return result;
}

void Log::rawStringToValue(std::shared_ptr<QString> &rawString) const {
  if (m_global_highlight == nullptr) {
    throw std::logic_error("m_global_highlight==nullptr");
  }
  rawString->replace('<', "<");
  rawString->replace('>', ">");
  rawString->replace(' ', " "); // html eats white spaces
  for (auto it = m_global_highlight->begin(); it != m_global_highlight->end(); ++it) {
    heighlightStr(rawString.get(), *it);
  }
}

std::shared_ptr<QString> Log::makeString(int row) const {
  auto result = makeRawString(row);
  rawStringToValue(result);
  return result;
}

int Log::rowCount(const QModelIndex &parent) const {
  //qDebug() << "Log::rowCount";
  std::lock_guard<std::mutex> lg(_locker);
  Q_UNUSED(parent);
  if (!m_load_complete) {
    return 0;
  }
  if (_fltr != nullptr) {
    return m_fltr_cache.size();
  }
  return m_lines.size();
  // return m_cache.size();
}

QVariant Log::data(const QModelIndex &index, int role) const {

  const QString emptyString = "EmptyString";
  if (!m_load_complete) {
    return emptyString;
  }

  if (index.row() < 0 || index.row() >= int(m_lines.size()))
    return QVariant();
  if (role == Qt::DisplayRole || role == Qt::EditRole) {
    //qDebug() << "Log::data";
    std::lock_guard<std::mutex> lg(_locker);
    if (_fltr != nullptr) {
      return *m_fltr_cache[index.row()].Value;
    }
    if (m_cache[index.row()].Value != nullptr) {
      return *m_cache[index.row()].Value;
    } else {
      CachedString cs;
      cs.originValue = makeRawString(index.row());
      cs.index = index.row();
      rawStringToValue(cs.originValue);
      cs.Value = cs.originValue;
      m_cache[index.row()] = cs;
      return *cs.Value;
    }
  }
  return QVariant();
}

QString Log::plainText(const QModelIndex &index) const {
  if (index.row() < 0 || index.row() >= int(m_lines.size()))
    return QString("error");
  if (_fltr == nullptr) {
    return *makeRawString(index.row());
  } else {
    return *makeRawString(m_fltr_cache[index.row()].index);
  }
}

void Log::clearHightlight() {
  //    m_load_complete=false;
  //    this->beginResetModel();
  //    QtConcurrent::blockingMap(m_buffer,[](CachedString&cs){
  //        cs.Value=cs.originValue;
  //    });
  //    for(int k=0;k<m_buffer.size();++k){
  //        auto mi=this->createIndex(k,0);
  //        dataChanged(mi, mi);
  //    }
  //    m_load_complete=true;
  //    this->endResetModel();
}

bool Log::heighlightStr(QString *str, const HighlightPattern &pattern) {
  if (pattern.pattern.size() == 0) {
    return false;
  }
  bool result = false;

  QRegExp re(pattern.pattern);
  if (re.indexIn(*str) != -1) {
    auto ct = re.capturedTexts();
    for (auto &&captured_str : ct) {
      str->replace(re,
                   "" + captured_str +
                       "");
    }
    result = true;
  }

  return result;
}

void Log::updateHeighlights(QVector<CachedString>::iterator /*begin*/,
                            QVector<CachedString>::iterator /*end*/,
                            const QString &pattern) {
  if (pattern.size() == 0) {
    return;
  }
  auto curDT = QDateTime::currentDateTimeUtc();

  qDebug() << m_fname << "updateHeighlights elapsed time:"
           << curDT.secsTo(QDateTime::currentDateTimeUtc());
}

void Log::updateHeighlights(const QString & /*pattern*/) {
  // updateHeighlights(m_buffer.begin(), m_buffer.end(), pattern);
}

void Log::localHightlightPattern(const QString &pattern) {
  if (pattern.size() == 0) {
    return;
  }
  updateHeighlights(pattern);
}

void Log::setListVoxObject(QListView *object) {
  m_lv_object = object;
}

QPair<int, QString> Log::findFrom(const QString &pattern, int index,
                                  SearchDirection direction) {
  qDebug() << "Log::findFrom";
  std::lock_guard<std::mutex> lg(_locker);
  // TODO make thread safety. m_cache can be brokent, when timer is active
  if (index == m_cache.size() || index < 0) {
    return QPair<int, QString>(index, QString());
  }

  QRegExp re(pattern.toUpper());

  int i = direction == SearchDirection::Down ? index + 1 : index - 1;

  while (true) {
    QString str = plainText(createIndex(int(i), 0, nullptr)).toUpper();
    if (re.indexIn(str) != -1) {
      return QPair<int, QString>(i, str);
    }
    if (direction == SearchDirection::Down) {
      i++;
      if (i == m_cache.size()) {
        break;
      }
    } else {
      i--;
      if (i < 0) {
        break;
      }
    }
  }
  return QPair<int, QString>(index, "");
}

void Log::resetFilter(const Filter_Ptr &fltr) {
  qDebug() << "Log::resetFilter";
  std::lock_guard<std::mutex> lg(_locker);
  m_cache.resize(m_lines.size());
  setFilter_impl(fltr);
}

void Log::setFilter(const Filter_Ptr &fltr) {
  qDebug() << "Log::setFilter";
  std::lock_guard<std::mutex> lg(_locker);
  setFilter_impl(fltr);
}

void Log::setFilter_impl(const Filter_Ptr &fltr) {
  _fltr = fltr;
  int count = 0;
  m_fltr_cache.resize(m_lines.size());
  for (size_t i = 0; i < m_lines.size(); ++i) {
    auto qs = makeRawString(i);
    if (fltr->inFilter(*qs)) {
      rawStringToValue(qs);
      CachedString cs;
      cs.index = i;
      cs.originValue = qs;
      cs.Value = cs.originValue;
      m_fltr_cache[count] = cs;
      count++;
    }
  }

  beginResetModel();
  m_fltr_cache.resize(count);
  endResetModel();
}

void Log::clearFilter() {
  qDebug() << "Log::clearFilter";
  if (_fltr != nullptr) {
    beginResetModel();
    _fltr = nullptr;
    m_fltr_cache.resize(1);
    endResetModel();
  }
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
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 its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/http_parser.hpp"

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/thread/mutex.hpp>
#include <cstdlib>
#include <boost/config.hpp>

using boost::bind;
using namespace libtorrent;

namespace libtorrent
{
	// defined in broadcast_socket.cpp
	address guess_local_address(asio::io_service&);
}

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(0)
	, m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143"), 6771)
		, bind(&lsd::on_announce, self(), _1, _2, _3))
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
}

lsd::~lsd() {}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	std::stringstream btsearch;
	btsearch << "BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: " << listen_port << "\r\n"
		"Infohash: " << ih << "\r\n"
		"\r\n\r\n";
	std::string const& msg = btsearch.str();

	m_retry_count = 0;
	asio::error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " ==> announce: ih: " << ih << " port: " << listen_port << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::resend_announce(asio::error_code const& e, std::string msg)
{
	if (e) return;

	asio::error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::on_announce(udp::endpoint const& from, char* buffer
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;

	http_parser p;

	bool error = false;
	p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
		, error);

	if (!p.header_finished() || error)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: incomplete HTTP message\n";
#endif
		return;
	}

	if (p.method() != "bt-search")
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid HTTP method: " << p.method() << std::endl;
#endif
		return;
	}

	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
		return;
	}

	std::string const& ih_str = p.header("infohash");
	if (ih_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
		return;
	}

	sha1_hash ih(0);
	std::istringstream ih_sstr(ih_str);
	ih_sstr >> ih;
	int port = atoi(port_str.c_str());

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " *** incoming local announce " << from.address()
			<< ":" << port << " ih: " << ih << std::endl;
#endif
		// we got an announce, pass it on through the callback
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
			m_callback(tcp::endpoint(from.address(), port), ih);
#ifndef BOOST_NO_EXCEPTIONS
		}
		catch (std::exception&) {}
#endif
	}
}

void lsd::close()
{
	m_socket.close();
	asio::error_code ec;
	m_broadcast_timer.cancel(ec);
	m_disabled = true;
	m_callback.clear();
}

<commit_msg>local service discovert resend fix<commit_after>/*

Copyright (c) 2007, Arvid Norberg
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 its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/http_parser.hpp"

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/thread/mutex.hpp>
#include <cstdlib>
#include <boost/config.hpp>

using boost::bind;
using namespace libtorrent;

namespace libtorrent
{
	// defined in broadcast_socket.cpp
	address guess_local_address(asio::io_service&);
}

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(1)
	, m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143"), 6771)
		, bind(&lsd::on_announce, self(), _1, _2, _3))
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
}

lsd::~lsd() {}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	std::stringstream btsearch;
	btsearch << "BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: " << listen_port << "\r\n"
		"Infohash: " << ih << "\r\n"
		"\r\n\r\n";
	std::string const& msg = btsearch.str();

	m_retry_count = 1;
	asio::error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " ==> announce: ih: " << ih << " port: " << listen_port << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::resend_announce(asio::error_code const& e, std::string msg)
{
	if (e) return;

	asio::error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::on_announce(udp::endpoint const& from, char* buffer
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;

	http_parser p;

	bool error = false;
	p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
		, error);

	if (!p.header_finished() || error)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: incomplete HTTP message\n";
#endif
		return;
	}

	if (p.method() != "bt-search")
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid HTTP method: " << p.method() << std::endl;
#endif
		return;
	}

	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
		return;
	}

	std::string const& ih_str = p.header("infohash");
	if (ih_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
		return;
	}

	sha1_hash ih(0);
	std::istringstream ih_sstr(ih_str);
	ih_sstr >> ih;
	int port = atoi(port_str.c_str());

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		m_log << time_now_string()
			<< " *** incoming local announce " << from.address()
			<< ":" << port << " ih: " << ih << std::endl;
#endif
		// we got an announce, pass it on through the callback
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
			m_callback(tcp::endpoint(from.address(), port), ih);
#ifndef BOOST_NO_EXCEPTIONS
		}
		catch (std::exception&) {}
#endif
	}
}

void lsd::close()
{
	m_socket.close();
	asio::error_code ec;
	m_broadcast_timer.cancel(ec);
	m_disabled = true;
	m_callback.clear();
}

<|endoftext|>


<commit_before>/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_series_data.h"
#include "qwt_math.h"

static inline QRectF qwtBoundingRect( const QPointF &sample )
{
    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtPoint3D &sample )
{
    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtPointPolar &sample )
{
    return QRectF( sample.azimuth(), sample.radius(), 0.0, 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtIntervalSample &sample )
{
    return QRectF( sample.interval.minValue(), sample.value,
        sample.interval.maxValue() - sample.interval.minValue(), 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtSetSample &sample )
{
    double minY = sample.set[0];
    double maxY = sample.set[0];

    for ( int i = 1; i < sample.set.size(); i++ )
    {
        if ( sample.set[i] < minY )
            minY = sample.set[i];
        if ( sample.set[i] > maxY )
            maxY = sample.set[i];
    }

    double minX = sample.value;
    double maxX = sample.value;

    return QRectF( minX, minY, maxX - minX, maxY - minY );
}

static inline QRectF qwtBoundingRect( const QwtOHLCSample &sample )
{
    const QwtInterval interval = sample.boundingInterval();
    return QRectF( interval.minValue(), sample.time, interval.width(), 0.0 );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/

template <class T>
QRectF qwtBoundingRectT(
    const QwtSeriesData<T>& series, int from, int to )
{
    QRectF boundingRect( 1.0, 1.0, -2.0, -2.0 ); // invalid;

    if ( from < 0 )
        from = 0;

    if ( to < 0 )
        to = series.size() - 1;

    if ( to < from )
        return boundingRect;

    int i;
    for ( i = from; i <= to; i++ )
    {
        const QRectF rect = qwtBoundingRect( series.sample( i ) );
        if ( rect.width() >= 0.0 && rect.height() >= 0.0 )
        {
            boundingRect = rect;
            i++;
            break;
        }
    }

    for ( ; i <= to; i++ )
    {
        const QRectF rect = qwtBoundingRect( series.sample( i ) );
        if ( rect.width() >= 0.0 && rect.height() >= 0.0 )
        {
            boundingRect.setLeft( qMin( boundingRect.left(), rect.left() ) );
            boundingRect.setRight( qMax( boundingRect.right(), rect.right() ) );
            boundingRect.setTop( qMin( boundingRect.top(), rect.top() ) );
            boundingRect.setBottom( qMax( boundingRect.bottom(), rect.bottom() ) );
        }
    }

    return boundingRect;
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QPointF> &series, int from, int to )
{
    return qwtBoundingRectT<QPointF>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtPoint3D> &series, int from, int to )
{
    return qwtBoundingRectT<QwtPoint3D>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  The horizontal coordinates represent the azimuth, the
  vertical coordinates the radius.

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtPointPolar> &series, int from, int to )
{
    return qwtBoundingRectT<QwtPointPolar>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtIntervalSample>& series, int from, int to )
{
    return qwtBoundingRectT<QwtIntervalSample>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtOHLCSample>& series, int from, int to )
{
    return qwtBoundingRectT<QwtOHLCSample>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtSetSample>& series, int from, int to )
{
    return qwtBoundingRectT<QwtSetSample>( series, from, to );
}

/*!
   Constructor
   \param samples Samples
*/
QwtPointSeriesData::QwtPointSeriesData(
        const QVector<QPointF> &samples ):
    QwtArraySeriesData<QPointF>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtPointSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtPoint3DSeriesData::QwtPoint3DSeriesData(
        const QVector<QwtPoint3D> &samples ):
    QwtArraySeriesData<QwtPoint3D>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtPoint3DSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtIntervalSeriesData::QwtIntervalSeriesData(
        const QVector<QwtIntervalSample> &samples ):
    QwtArraySeriesData<QwtIntervalSample>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtIntervalSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtSetSeriesData::QwtSetSeriesData(
        const QVector<QwtSetSample> &samples ):
    QwtArraySeriesData<QwtSetSample>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtSetSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtTradingChartData::QwtTradingChartData(
        const QVector<QwtOHLCSample> &samples ):
    QwtArraySeriesData<QwtOHLCSample>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtTradingChartData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}
<commit_msg>pmchart: fix chart auto-scaling under fetch error conditions<commit_after>/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 * Copyright (C) 1997   Josef Wilgen
 * Copyright (C) 2002   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_series_data.h"
#include "qwt_math.h"

static inline QRectF qwtBoundingRect( const QPointF &sample )
{
    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtPoint3D &sample )
{
    return QRectF( sample.x(), sample.y(), 0.0, 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtPointPolar &sample )
{
    return QRectF( sample.azimuth(), sample.radius(), 0.0, 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtIntervalSample &sample )
{
    return QRectF( sample.interval.minValue(), sample.value,
        sample.interval.maxValue() - sample.interval.minValue(), 0.0 );
}

static inline QRectF qwtBoundingRect( const QwtSetSample &sample )
{
    double minY = sample.set[0];
    double maxY = sample.set[0];

    for ( int i = 1; i < sample.set.size(); i++ )
    {
        if ( sample.set[i] < minY )
            minY = sample.set[i];
        if ( sample.set[i] > maxY )
            maxY = sample.set[i];
    }

    double minX = sample.value;
    double maxX = sample.value;

    return QRectF( minX, minY, maxX - minX, maxY - minY );
}

static inline QRectF qwtBoundingRect( const QwtOHLCSample &sample )
{
    const QwtInterval interval = sample.boundingInterval();
    return QRectF( interval.minValue(), sample.time, interval.width(), 0.0 );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series. Ignores samples that
  are NaN as they do not lie within a finite bounding box.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
static inline bool sampleBoundValid(QRectF rect)
{
    if ( qIsNaN(rect.left()) || qIsNaN(rect.right()) ||
         qIsNaN(rect.top()) || qIsNaN(rect.bottom()) )
        return false;
    if ( rect.width() < 0.0 || rect.height() < 0.0 )
        return false;
    return true;
}

template <class T>
QRectF qwtBoundingRectT(
    const QwtSeriesData<T>& series, int from, int to )
{
    QRectF boundingRect( 1.0, 1.0, -2.0, -2.0 ); // invalid;

    if ( from < 0 )
        from = 0;

    if ( to < 0 )
        to = series.size() - 1;

    if ( to < from )
        return boundingRect;

    int i;
    for ( i = from; i <= to; i++ )
    {
        const QRectF rect = qwtBoundingRect( series.sample( i ) );
        if ( sampleBoundValid( rect ) )
        {
            boundingRect = rect;
            i++;
            break;
        }
    }

    for ( ; i <= to; i++ )
    {
        const QRectF rect = qwtBoundingRect( series.sample( i ) );
        if ( sampleBoundValid( rect ) )
        {
            boundingRect.setLeft( qMin( boundingRect.left(), rect.left() ) );
            boundingRect.setRight( qMax( boundingRect.right(), rect.right() ) );
            boundingRect.setTop( qMin( boundingRect.top(), rect.top() ) );
            boundingRect.setBottom( qMax( boundingRect.bottom(), rect.bottom() ) );
        }
    }

    return boundingRect;
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QPointF> &series, int from, int to )
{
    return qwtBoundingRectT<QPointF>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtPoint3D> &series, int from, int to )
{
    return qwtBoundingRectT<QwtPoint3D>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  The horizontal coordinates represent the azimuth, the
  vertical coordinates the radius.

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtPointPolar> &series, int from, int to )
{
    return qwtBoundingRectT<QwtPointPolar>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtIntervalSample>& series, int from, int to )
{
    return qwtBoundingRectT<QwtIntervalSample>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtOHLCSample>& series, int from, int to )
{
    return qwtBoundingRectT<QwtOHLCSample>( series, from, to );
}

/*!
  \brief Calculate the bounding rectangle of a series subset

  Slow implementation, that iterates over the series.

  \param series Series
  \param from Index of the first sample, <= 0 means from the beginning
  \param to Index of the last sample, < 0 means to the end

  \return Bounding rectangle
*/
QRectF qwtBoundingRect(
    const QwtSeriesData<QwtSetSample>& series, int from, int to )
{
    return qwtBoundingRectT<QwtSetSample>( series, from, to );
}

/*!
   Constructor
   \param samples Samples
*/
QwtPointSeriesData::QwtPointSeriesData(
        const QVector<QPointF> &samples ):
    QwtArraySeriesData<QPointF>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtPointSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtPoint3DSeriesData::QwtPoint3DSeriesData(
        const QVector<QwtPoint3D> &samples ):
    QwtArraySeriesData<QwtPoint3D>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtPoint3DSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtIntervalSeriesData::QwtIntervalSeriesData(
        const QVector<QwtIntervalSample> &samples ):
    QwtArraySeriesData<QwtIntervalSample>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtIntervalSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtSetSeriesData::QwtSetSeriesData(
        const QVector<QwtSetSample> &samples ):
    QwtArraySeriesData<QwtSetSample>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtSetSeriesData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}

/*!
   Constructor
   \param samples Samples
*/
QwtTradingChartData::QwtTradingChartData(
        const QVector<QwtOHLCSample> &samples ):
    QwtArraySeriesData<QwtOHLCSample>( samples )
{
}

/*!
  \brief Calculate the bounding rectangle

  The bounding rectangle is calculated once by iterating over all
  points and is stored for all following requests.

  \return Bounding rectangle
*/
QRectF QwtTradingChartData::boundingRect() const
{
    if ( d_boundingRect.width() < 0.0 )
        d_boundingRect = qwtBoundingRect( *this );

    return d_boundingRect;
}
<|endoftext|>


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

#include "paddle/fluid/operators/sum_op.h"

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "paddle/fluid/framework/var_type_inference.h"
#include "paddle/fluid/operators/detail/safe_ref.h"

#ifdef PADDLE_WITH_MKLDNN
#include "paddle/fluid/platform/mkldnn_helper.h"
#endif

namespace paddle {
namespace operators {
using framework::Tensor;

class SumOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInputs("X"), "Inputs(X) should not be null");

    PADDLE_ENFORCE(ctx->HasOutput("Out"),
                   "Output(Out) of SumOp should not be null.");
    if (ctx->IsRuntime() &&
        ctx->GetOutputsVarType("Out")[0] ==
            framework::proto::VarType::LOD_TENSOR_ARRAY) {
      return;  // skip runtime infershape when is tensor array;
    }

    auto x_var_types = ctx->GetInputsVarType("X");
    auto x_dims = ctx->GetInputsDim("X");

    size_t N = x_dims.size();
    PADDLE_ENFORCE_GT(N, 0, "Input tensors count should > 0.");
    if (N == 1) {
      VLOG(3) << "Warning: sum have only one input, may waste memory";
    }

    framework::DDim in_dim({0});
    for (size_t i = 0; i < x_dims.size(); ++i) {
      auto& x_dim = x_dims[i];
      // x_dim.size() == 1 means the real dim of selected rows is [0]
      if (x_var_types[i] == framework::proto::VarType::SELECTED_ROWS &&
          x_dim.size() == 1) {
        continue;
      }
      if (framework::product(x_dim) == 0) {
        continue;
      }
      if (framework::product(in_dim) == 0) {
        in_dim = x_dim;
      } else {
        PADDLE_ENFORCE_EQ(in_dim, x_dim, "Input tensors must have same shape");
      }
    }
    ctx->SetOutputDim("Out", in_dim);
    ctx->ShareLoD("X", /*->*/ "Out");
  }

 protected:
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    auto x_vars = ctx.MultiInputVar("X");
    auto x_vars_name = ctx.Inputs("X");

    framework::LibraryType library{framework::LibraryType::kPlain};
    framework::DataLayout layout{framework::DataLayout::kAnyLayout};

#ifdef PADDLE_WITH_MKLDNN
    if (library == framework::LibraryType::kPlain &&
        platform::CanMKLDNNBeUsed(ctx)) {
      library = framework::LibraryType::kMKLDNN;
      layout = framework::DataLayout::kMKLDNN;
    }
#endif

    if (x_vars[0]->IsType<framework::LoDTensor>()) {
      int dtype = -1;
      for (size_t idx = 0; idx < x_vars.size(); ++idx) {
        PADDLE_ENFORCE(x_vars[idx] != nullptr,
                       "Input var[%s] should not be nullptr", x_vars_name[idx]);
        auto tensor =
            framework::GetLoDTensorOrSelectedRowsValueFromVar(*x_vars[idx]);
        if (tensor->numel() == 0) {
          continue;
        }
        if (dtype == -1) {
          dtype = tensor->type();
        } else {
          PADDLE_ENFORCE_EQ(dtype, tensor->type());
        }
      }
      PADDLE_ENFORCE_NE(dtype, -1,
                        "Sum operator should have at least one tensor");

      return framework::OpKernelType(
          static_cast<framework::proto::VarType::Type>(dtype), ctx.GetPlace(),
          layout, library);
    } else if (x_vars[0]->IsType<framework::SelectedRows>()) {
      for (auto& var : x_vars) {
        auto& value = var->Get<framework::SelectedRows>().value();
        if (value.IsInitialized()) {
          return framework::OpKernelType(value.type(), ctx.device_context(),
                                         layout, library);
        }
      }
      // if input sparse vars are not initialized, use an default kernel type.
      return framework::OpKernelType(framework::proto::VarType::FP32,
                                     ctx.device_context(), layout, library);
    } else if (x_vars[0]->IsType<framework::LoDTensorArray>()) {
      for (auto& x_var : x_vars) {
        auto& array = x_var->Get<framework::LoDTensorArray>();
        for (auto& each : array) {
          if (each.numel() != 0) {
            return framework::OpKernelType(each.type(), ctx.device_context(),
                                           layout, library);
          }
        }
      }
      PADDLE_THROW("Cannot find the input data type by all input data");
    }
    PADDLE_THROW("Unexpected branch. Input type is %s",
                 framework::ToTypeName(x_vars[0]->Type()));
  }
};

class SumOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("X", "(vector<Tensor>) The input tensors of sum operator.")
        .AsDuplicable();
    AddOutput("Out", "(Tensor) The output tensor of sum operator.");
    AddAttr<bool>("use_mkldnn",
                  "(bool, default false) Only used in mkldnn kernel")
        .SetDefault(false);
    AddComment(R"DOC(
Sum operator.

This operators sums the input tensors. All the inputs can carry the
LoD (Level of Details) information. However, the output only shares
the LoD information with the first input.
)DOC");
  }
};

class SumOpVarTypeInference : public framework::VarTypeInference {
 public:
  void operator()(framework::InferVarTypeContext* ctx) const override {
    auto& inputs = ctx->Input("X");
    auto var_type = framework::proto::VarType::SELECTED_ROWS;
    for (auto& name : ctx->Input("X")) {
      VLOG(10) << name << " " << ctx->GetType(name);
    }

    bool any_input_is_lod_tensor = std::any_of(
        inputs.begin(), inputs.end(), [ctx](const std::string& name) {
          return ctx->GetType(name) == framework::proto::VarType::LOD_TENSOR;
        });

    auto is_tensor_array = [ctx](const std::string& name) {
      return ctx->GetType(name) == framework::proto::VarType::LOD_TENSOR_ARRAY;
    };

    bool any_input_is_tensor_array =
        std::any_of(inputs.begin(), inputs.end(), is_tensor_array);
    bool all_inputs_are_tensor_array =
        std::all_of(inputs.begin(), inputs.end(), is_tensor_array);

    if (any_input_is_tensor_array) {
      if (!all_inputs_are_tensor_array) {
        std::ostringstream os;
        for (auto& each : inputs) {
          os << "    " << each << " type is " << ctx->GetType(each) << "\n";
        }
        PADDLE_ENFORCE(all_inputs_are_tensor_array,
                       "Not all inputs are tensor array:\n%s", os.str());
      }
      var_type = framework::proto::VarType::LOD_TENSOR_ARRAY;
    } else if (any_input_is_lod_tensor) {
      var_type = framework::proto::VarType::LOD_TENSOR;
    }

    auto out_var_name = ctx->Output("Out").front();
    ctx->SetType(out_var_name, var_type);
    ctx->SetDataType(out_var_name, ctx->GetDataType(inputs.front()));
  }
};

class SumGradMaker : public framework::GradOpDescMakerBase {
 public:
  using framework::GradOpDescMakerBase::GradOpDescMakerBase;

  std::vector<std::unique_ptr<framework::OpDesc>> operator()() const override {
    auto x_grads = InputGrad("X", false);
    std::vector<std::unique_ptr<framework::OpDesc>> grad_ops;
    grad_ops.reserve(x_grads.size());
    auto og = OutputGrad("Out");
    std::transform(x_grads.begin(), x_grads.end(), std::back_inserter(grad_ops),
                   [&og](const std::string& x_grad) {
                     auto* grad_op = new framework::OpDesc();
                     grad_op->SetType("scale");
                     grad_op->SetInput("X", og);
                     grad_op->SetOutput("Out", {x_grad});
                     grad_op->SetAttr("scale", 1.0f);
                     return std::unique_ptr<framework::OpDesc>(grad_op);
                   });
    return grad_ops;
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;

REGISTER_OPERATOR(sum, ops::SumOp, ops::SumOpMaker, ops::SumGradMaker,
                  ops::SumOpVarTypeInference);

REGISTER_OP_CPU_KERNEL(
    sum, ops::SumKernel<paddle::platform::CPUDeviceContext, float>,
    ops::SumKernel<paddle::platform::CPUDeviceContext, double>,
    ops::SumKernel<paddle::platform::CPUDeviceContext, int>,
    ops::SumKernel<paddle::platform::CPUDeviceContext, int64_t>);
<commit_msg>Fix sum infershape issue<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */

#include "paddle/fluid/operators/sum_op.h"

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include "paddle/fluid/framework/var_type_inference.h"
#include "paddle/fluid/operators/detail/safe_ref.h"

#ifdef PADDLE_WITH_MKLDNN
#include "paddle/fluid/platform/mkldnn_helper.h"
#endif

namespace paddle {
namespace operators {
using framework::Tensor;

class SumOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInputs("X"), "Inputs(X) should not be null");

    PADDLE_ENFORCE(ctx->HasOutput("Out"),
                   "Output(Out) of SumOp should not be null.");
    if (ctx->IsRuntime() &&
        ctx->GetOutputsVarType("Out")[0] ==
            framework::proto::VarType::LOD_TENSOR_ARRAY) {
      return;  // skip runtime infershape when is tensor array;
    }

    auto x_var_types = ctx->GetInputsVarType("X");
    auto x_dims = ctx->GetInputsDim("X");

    size_t N = x_dims.size();
    PADDLE_ENFORCE_GT(N, 0, "Input tensors count should > 0.");
    if (N == 1) {
      VLOG(3) << "Warning: sum have only one input, may waste memory";
    }

    framework::DDim in_dim({0});
    for (size_t i = 0; i < x_dims.size(); ++i) {
      auto& x_dim = x_dims[i];
      // x_dim.size() == 1 means the real dim of selected rows is [0]
      if (x_var_types[i] == framework::proto::VarType::SELECTED_ROWS &&
          x_dim.size() == 1) {
        continue;
      }
      if (framework::product(x_dim) == 0) {
        continue;
      }
      if (framework::product(in_dim) == 0) {
        in_dim = x_dim;
      } else {
        if (ctx->IsRuntime()) {
          PADDLE_ENFORCE_EQ(in_dim, x_dim,
                            "Input tensors must have same shape");
        } else {
          PADDLE_ENFORCE_EQ(in_dim.size(), x_dim.size(),
                            "Input tensors must have same shape size");
          // if in_dim or x_dim has -1, not check equal
          for (int i = 0; i < x_dim.size(); ++i) {
            if (x_dim[i] == -1 || in_dim[i] == -1) {
              continue;
            }
            PADDLE_ENFORCE_EQ(in_dim[i], x_dim[i],
                              "Input tensors must have same shape if not -1");
          }
        }
      }
    }
    ctx->SetOutputDim("Out", in_dim);
    ctx->ShareLoD("X", /*->*/ "Out");
  }

 protected:
  framework::OpKernelType GetExpectedKernelType(
      const framework::ExecutionContext& ctx) const override {
    auto x_vars = ctx.MultiInputVar("X");
    auto x_vars_name = ctx.Inputs("X");

    framework::LibraryType library{framework::LibraryType::kPlain};
    framework::DataLayout layout{framework::DataLayout::kAnyLayout};

#ifdef PADDLE_WITH_MKLDNN
    if (library == framework::LibraryType::kPlain &&
        platform::CanMKLDNNBeUsed(ctx)) {
      library = framework::LibraryType::kMKLDNN;
      layout = framework::DataLayout::kMKLDNN;
    }
#endif

    if (x_vars[0]->IsType<framework::LoDTensor>()) {
      int dtype = -1;
      for (size_t idx = 0; idx < x_vars.size(); ++idx) {
        PADDLE_ENFORCE(x_vars[idx] != nullptr,
                       "Input var[%s] should not be nullptr", x_vars_name[idx]);
        auto tensor =
            framework::GetLoDTensorOrSelectedRowsValueFromVar(*x_vars[idx]);
        if (tensor->numel() == 0) {
          continue;
        }
        if (dtype == -1) {
          dtype = tensor->type();
        } else {
          PADDLE_ENFORCE_EQ(dtype, tensor->type());
        }
      }
      PADDLE_ENFORCE_NE(dtype, -1,
                        "Sum operator should have at least one tensor");

      return framework::OpKernelType(
          static_cast<framework::proto::VarType::Type>(dtype), ctx.GetPlace(),
          layout, library);
    } else if (x_vars[0]->IsType<framework::SelectedRows>()) {
      for (auto& var : x_vars) {
        auto& value = var->Get<framework::SelectedRows>().value();
        if (value.IsInitialized()) {
          return framework::OpKernelType(value.type(), ctx.device_context(),
                                         layout, library);
        }
      }
      // if input sparse vars are not initialized, use an default kernel type.
      return framework::OpKernelType(framework::proto::VarType::FP32,
                                     ctx.device_context(), layout, library);
    } else if (x_vars[0]->IsType<framework::LoDTensorArray>()) {
      for (auto& x_var : x_vars) {
        auto& array = x_var->Get<framework::LoDTensorArray>();
        for (auto& each : array) {
          if (each.numel() != 0) {
            return framework::OpKernelType(each.type(), ctx.device_context(),
                                           layout, library);
          }
        }
      }
      PADDLE_THROW("Cannot find the input data type by all input data");
    }
    PADDLE_THROW("Unexpected branch. Input type is %s",
                 framework::ToTypeName(x_vars[0]->Type()));
  }
};

class SumOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("X", "(vector<Tensor>) The input tensors of sum operator.")
        .AsDuplicable();
    AddOutput("Out", "(Tensor) The output tensor of sum operator.");
    AddAttr<bool>("use_mkldnn",
                  "(bool, default false) Only used in mkldnn kernel")
        .SetDefault(false);
    AddComment(R"DOC(
Sum operator.

This operators sums the input tensors. All the inputs can carry the
LoD (Level of Details) information. However, the output only shares
the LoD information with the first input.
)DOC");
  }
};

class SumOpVarTypeInference : public framework::VarTypeInference {
 public:
  void operator()(framework::InferVarTypeContext* ctx) const override {
    auto& inputs = ctx->Input("X");
    auto var_type = framework::proto::VarType::SELECTED_ROWS;
    for (auto& name : ctx->Input("X")) {
      VLOG(10) << name << " " << ctx->GetType(name);
    }

    bool any_input_is_lod_tensor = std::any_of(
        inputs.begin(), inputs.end(), [ctx](const std::string& name) {
          return ctx->GetType(name) == framework::proto::VarType::LOD_TENSOR;
        });

    auto is_tensor_array = [ctx](const std::string& name) {
      return ctx->GetType(name) == framework::proto::VarType::LOD_TENSOR_ARRAY;
    };

    bool any_input_is_tensor_array =
        std::any_of(inputs.begin(), inputs.end(), is_tensor_array);
    bool all_inputs_are_tensor_array =
        std::all_of(inputs.begin(), inputs.end(), is_tensor_array);

    if (any_input_is_tensor_array) {
      if (!all_inputs_are_tensor_array) {
        std::ostringstream os;
        for (auto& each : inputs) {
          os << "    " << each << " type is " << ctx->GetType(each) << "\n";
        }
        PADDLE_ENFORCE(all_inputs_are_tensor_array,
                       "Not all inputs are tensor array:\n%s", os.str());
      }
      var_type = framework::proto::VarType::LOD_TENSOR_ARRAY;
    } else if (any_input_is_lod_tensor) {
      var_type = framework::proto::VarType::LOD_TENSOR;
    }

    auto out_var_name = ctx->Output("Out").front();
    ctx->SetType(out_var_name, var_type);
    ctx->SetDataType(out_var_name, ctx->GetDataType(inputs.front()));
  }
};

class SumGradMaker : public framework::GradOpDescMakerBase {
 public:
  using framework::GradOpDescMakerBase::GradOpDescMakerBase;

  std::vector<std::unique_ptr<framework::OpDesc>> operator()() const override {
    auto x_grads = InputGrad("X", false);
    std::vector<std::unique_ptr<framework::OpDesc>> grad_ops;
    grad_ops.reserve(x_grads.size());
    auto og = OutputGrad("Out");
    std::transform(x_grads.begin(), x_grads.end(), std::back_inserter(grad_ops),
                   [&og](const std::string& x_grad) {
                     auto* grad_op = new framework::OpDesc();
                     grad_op->SetType("scale");
                     grad_op->SetInput("X", og);
                     grad_op->SetOutput("Out", {x_grad});
                     grad_op->SetAttr("scale", 1.0f);
                     return std::unique_ptr<framework::OpDesc>(grad_op);
                   });
    return grad_ops;
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;

REGISTER_OPERATOR(sum, ops::SumOp, ops::SumOpMaker, ops::SumGradMaker,
                  ops::SumOpVarTypeInference);

REGISTER_OP_CPU_KERNEL(
    sum, ops::SumKernel<paddle::platform::CPUDeviceContext, float>,
    ops::SumKernel<paddle::platform::CPUDeviceContext, double>,
    ops::SumKernel<paddle::platform::CPUDeviceContext, int>,
    ops::SumKernel<paddle::platform::CPUDeviceContext, int64_t>);
<|endoftext|>