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

#include <thrift/lib/cpp/util/kerberos/Krb5CCacheStore.h>

#include <glog/logging.h>
#include <memory>
#include <set>
#include <stdio.h>

#include <folly/stats/BucketedTimeSeries-defs.h>
#include <folly/Memory.h>
#include <folly/ScopeGuard.h>
#include <folly/String.h>

// In older version of krb, this constant is not defined. Define it
#ifndef KRB5_GC_NO_STORE
#define KRB5_GC_NO_STORE     8  /**< Do not store in credential cache */
#endif

namespace apache { namespace thrift { namespace krb5 {
using namespace std;

const int Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS = 10;
const int Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD = 600;

static bool serviceCountCompare (
    const pair<string, uint64_t>& i,
    const pair<string, uint64_t>& j) {
  return (i.second > j.second);
}

Krb5CCacheStore::ServiceData::ServiceData() :
  timeSeries(
    Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS,
    std::chrono::seconds(
      Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD)) {
}

void Krb5CCacheStore::ServiceData::bumpCount() {
  folly::SharedMutex::WriteHolder guard(lockTimeSeries);
  time_t now = time(nullptr);
  timeSeries.addValue(std::chrono::seconds(now), 1);
}

uint64_t Krb5CCacheStore::ServiceData::getCount() {
  folly::SharedMutex::ReadHolder guard(lockTimeSeries);
  // Note that we don't have a need to call timeSeries_.update(<time>)
  // here because we don't care to have the exact count at the current
  // time. We're ok with grabbing the count at the last update.
  return timeSeries.count();
}

std::shared_ptr<Krb5CCache> Krb5CCacheStore::waitForCache(
    const Krb5Principal& service,
    SecurityLogger* logger) {
  std::shared_ptr<ServiceData> dataPtr = getServiceDataPtr(service);

  // Bump that we've used the principal
  dataPtr->bumpCount();

  if (logger) {
    logger->logStart("get_prepared_cache", folly::to<string>(service));
  }

  {
    folly::SharedMutex::ReadHolder readLock(dataPtr->lockCache);
    // If there is a cache, just return it. Try with a read lock first
    // for performance reasons.
    if (dataPtr->cache) {
      if (logger) {
        logger->logEnd("get_prepared_cache");
      }
      return dataPtr->cache;
    }
  }

  // If we didn't find a cache, initialize a local, temporary cache for the
  // service. We might throw this away if some other thread initialized the
  // cache meanwhile.
  if (logger) {
    logger->logStart("init_cache_for_service", folly::to<string>(service));
  }
  auto tempCache = initCacheForService(service, nullptr, logger);
  if (logger) {
    logger->logEnd("init_cache_for_service");
  }

  // Upgrade to a write lock, and initialize the cache only if it is not already
  // initialized by some other thread meanwhile.
  folly::SharedMutex::WriteHolder writeLock(dataPtr->lockCache);
  if (!dataPtr->cache) {
    dataPtr->cache = std::move(tempCache);
  }
  return dataPtr->cache;
}

std::shared_ptr<Krb5CCacheStore::ServiceData>
    Krb5CCacheStore::getServiceDataPtr(const Krb5Principal& service) {

  // If ccache is disabled, just return a fresh ServiceData obj
  if (maxCacheSize_ == 0) {
    return std::make_shared<ServiceData>();
  }

  string service_name = folly::to<string>(service);
  {
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    auto found = serviceDataMap_.find(service_name);
    if (found != serviceDataMap_.end()) {
      // Get the element from the map if it exists
      return found->second;
    }
  }

  // Not found, we need to create it
  folly::SharedMutex::WriteHolder writeLock(serviceDataMapLock_);
  auto found = serviceDataMap_.find(service_name);
  if (found == serviceDataMap_.end()) {
    // If we reached the limit, then we need to free some room
    if (maxCacheSize_ > 0 && cacheItemQueue_.size() >= maxCacheSize_) {
      serviceDataMap_.erase(cacheItemQueue_.front());
      cacheItemQueue_.pop();
    }
    serviceDataMap_[service_name] = std::make_shared<ServiceData>();
    cacheItemQueue_.push(service_name);
  }
  return serviceDataMap_[service_name];
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::initCacheForService(
    const Krb5Principal& service,
    const krb5_creds* creds,
    SecurityLogger* logger) {

  if (logger) {
    logger->logStart("wait_for_tgt");
  }
  std::shared_ptr<const Krb5Credentials> tgt;
  auto client_princ = tgts_.getClientPrincipal();
  // Get a cross-realm tgt if we need
  string srealm = service.getRealm();
  if (srealm != tgts_.getClientPrincipal().getRealm()) {
    tgt = tgts_.getTgtForRealm(srealm);
  } else {
    tgt = tgts_.getTgt();
  }
  if (logger) {
    logger->logEnd("wait_for_tgt");
  }

  if (logger) {
    logger->logStart("init_barebones_ccache");
  }
  // Make a new memory cache.
  auto mem = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  mem->setDestroyOnClose();
  mem->initialize(client_princ.get());
  mem->storeCred(tgt->get());
  if (logger) {
    logger->logEnd("init_barebones_ccache");
  }

  if (creds != nullptr) {
    mem->storeCred(*creds);
  } else {
    if (logger) {
      logger->logStart("get_credential_from_kdc");
    }
    mem->getCredentials(service.get());
    if (logger) {
      logger->logEnd("get_credential_from_kdc");
    }
  }

  return mem;
}

void Krb5CCacheStore::importCache(
    Krb5CCache& file_cache) {
  logger_->logStart("import_tgts");
  // Split out tgts and service principals
  std::vector<Krb5Credentials> tgts;
  std::vector<Krb5Credentials> services;
  uint64_t count = 0;
  for (auto& tmp_cred : file_cache) {
    Krb5Credentials cred(std::move(tmp_cred));
    Krb5Principal server = Krb5Principal::copyPrincipal(
      ctx_.get(), cred.get().server);
    if (server.isTgt()) {
      tgts.push_back(std::move(cred));
      count++;
    } else if (!server.getRealm().empty()) {
      // Sometimes, somehow, principals for host/{hg.vvv,svn.vip} have no
      // realm!!  Why?  No idea, but they exist and they blow up the entire
      // cache reading thread, so defend against them for now by ignoring that
      // entry (example: #7808411).
      services.push_back(std::move(cred));
    } else if logger() {
      logger->log("Realm empty, so service ignored");
    }

  }

  // Import TGTs
  Krb5Tgts tgts_obj;
  Krb5Principal client_principal = file_cache.getClientPrincipal();
  tgts_obj.setClientPrincipal(client_principal);
  for (auto& tgt : tgts) {
    Krb5Principal server_principal = Krb5Principal::copyPrincipal(
      ctx_.get(), tgt.get().server);
    if (server_principal.getComponent(1) == client_principal.getRealm()) {
      tgts_obj.setTgt(folly::make_unique<Krb5Credentials>(std::move(tgt)));
    } else {
      tgts_obj.setTgtForRealm(
        server_principal.getComponent(1),
        folly::make_unique<Krb5Credentials>(std::move(tgt)));
    }
  }
  tgts_ = std::move(tgts_obj);
  logger_->logEnd("import_tgts", folly::to<std::string>(count));

  // Import service creds
  DataMapType new_data_map;
  std::queue<std::string> new_data_queue;
  logger_->logStart("import_service_creds");
  count = 0;
  for (auto& service : services) {
    if (maxCacheSize_ >= 0 && count >= maxCacheSize_) {
      break;
    }
    Krb5Principal princ = Krb5Principal::copyPrincipal(
      ctx_.get(), service.get().server);
    auto mem = initCacheForService(princ, &service.get());

    auto data = std::make_shared<ServiceData>();
    std::string name = folly::to<string>(princ);
    new_data_map[name] = data;
    new_data_queue.push(name);
    data->cache = std::move(mem);
    data->bumpCount();
    count++;
  }

  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_ = std::move(new_data_map);
  cacheItemQueue_ = std::move(new_data_queue);
  logger_->logEnd("import_service_creds", folly::to<std::string>(count));
}

std::vector<Krb5Principal> Krb5CCacheStore::getServicePrincipalList() {
  std::vector<Krb5Principal> services;
  folly::SharedMutex::ReadHolder lock(serviceDataMapLock_);
  for (auto& data : serviceDataMap_) {
    folly::SharedMutex::ReadHolder lock(data.second->lockCache);
    if (!data.second->cache) {
      continue;
    }
    auto princ_list = data.second->cache->getServicePrincipalList();
    if (princ_list.size() < 1) {
      throw std::runtime_error("Principal list too small in ccache");
    }
    auto princ = std::move(princ_list[0]);
    services.push_back(std::move(princ));
  }
  return services;
}

uint64_t Krb5CCacheStore::renewCreds() {
  auto curTgt = tgts_.getTgt();
  auto client_princ = tgts_.getClientPrincipal();
  auto realms = tgts_.getValidRealms();
  uint64_t renewCount = 0;

  // Renew TGTs
  Krb5Tgts tgts;
  tgts.kInit(client_princ);
  for (const auto& realm : realms) {
    tgts.getTgtForRealm(realm);
  }
  tgts_ = std::move(tgts);

  // Renew service creds, and store the renewed creds in a temporary map
  std::unordered_map<string, std::unique_ptr<Krb5CCache>> renewed_map;
  for (auto& service : getServicePrincipalList()) {
    try {
      auto mem = initCacheForService(service);
      renewed_map[folly::to<string>(service)] = std::move(mem);
      renewCount++;
    } catch (const std::runtime_error& e) {
      VLOG(4) << "Failed to renew cred for service: "
              << folly::to<string>(service) << " "
              << e.what();
    }
  }

  // Update the main service data map with the renewed creds.
  // If the creds failed to be renewed and the old ones are stale, then
  // just delete the old ones.
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  for (auto& entry : serviceDataMap_) {
    auto renewed_entry = renewed_map.find(entry.first);
    folly::SharedMutex::WriteHolder lock(entry.second->lockCache);
    if (renewed_entry != renewed_map.end()) {
      entry.second->cache = std::move(renewed_entry->second);
    } else if (entry.second->cache) {
      // Not found, see if it's a new cred or old one
      auto lifetime = entry.second->cache->getLifetime();
      auto tgt_lifetime = tgts_.getLifetime();
      if (lifetime.second < tgt_lifetime.second) {
        // Delete old credential
        entry.second->cache.reset();
      }
    } // else
    // It's possible this thread wins the race to hit a service in the
    // data map that hasn't yet obtained a ccache. In this case, just leave
    // it alone. It will get the correct ccache.
  }
  return renewCount;
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::exportCache(size_t limit) {
  Krb5Principal client_principal = tgts_.getClientPrincipal();

  // Make a new memory cache.
  auto temp_cache = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  temp_cache->initialize(client_principal.get());

  {
    // Put 'limit' number of most frequently used credentials into the
    // top_services set.
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    vector<pair<string, uint64_t>> count_vector;
    for (auto& element : serviceDataMap_) {
      count_vector.emplace_back(element.first, element.second->getCount());
    }

    sort(count_vector.begin(), count_vector.end(), serviceCountCompare);

    std::set<string> top_services;
    int count = 0;
    for (auto& element : count_vector) {
      if (count >= limit) {
        break;
      }
      top_services.insert(element.first);
      count++;
    }

    for (auto& data : serviceDataMap_) {
      folly::SharedMutex::ReadHolder lock(data.second->lockCache);
      if (!data.second->cache) {
        continue;
      }
      auto princ_list = data.second->cache->getServicePrincipalList();
      if (princ_list.size() < 1) {
        throw std::runtime_error("Principal list too small in ccache");
      }
      auto princ = std::move(princ_list[0]);
      if (top_services.count(folly::to<string>(princ)) == 0) {
        continue;
      }
      Krb5Credentials service_cred = data.second->cache->retrieveCred(
        princ.get());
      temp_cache->storeCred(service_cred.get());
    }
  }

  // Store the TGTs in the cache.
  temp_cache->storeCred(tgts_.getTgt()->get());
  auto realms = tgts_.getValidRealms();
  for (const auto& realm : realms) {
    auto cred = tgts_.getTgtForRealm(realm);
    temp_cache->storeCred(cred->get());
  }
  return temp_cache;
}

bool Krb5CCacheStore::isInitialized() {
  return tgts_.isInitialized();
}

std::pair<uint64_t, uint64_t> Krb5CCacheStore::getLifetime() {
  return tgts_.getLifetime();
}

Krb5Principal Krb5CCacheStore::getClientPrincipal() {
  return tgts_.getClientPrincipal();
}

void Krb5CCacheStore::kInit(const Krb5Principal& client) {
  tgts_.kInit(client);
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_.clear();
  std::queue<std::string> emptyQueue;
  cacheItemQueue_ = std::move(emptyQueue);
}

void Krb5CCacheStore::notifyOfError(const std::string& error) {
  tgts_.notifyOfError(error);
}

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

#include <thrift/lib/cpp/util/kerberos/Krb5CCacheStore.h>

#include <glog/logging.h>
#include <memory>
#include <set>
#include <stdio.h>

#include <folly/stats/BucketedTimeSeries-defs.h>
#include <folly/Memory.h>
#include <folly/ScopeGuard.h>
#include <folly/String.h>

// In older version of krb, this constant is not defined. Define it
#ifndef KRB5_GC_NO_STORE
#define KRB5_GC_NO_STORE     8  /**< Do not store in credential cache */
#endif

namespace apache { namespace thrift { namespace krb5 {
using namespace std;

const int Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS = 10;
const int Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD = 600;

static bool serviceCountCompare (
    const pair<string, uint64_t>& i,
    const pair<string, uint64_t>& j) {
  return (i.second > j.second);
}

Krb5CCacheStore::ServiceData::ServiceData() :
  timeSeries(
    Krb5CCacheStore::SERVICE_HISTOGRAM_NUM_BUCKETS,
    std::chrono::seconds(
      Krb5CCacheStore::SERVICE_HISTOGRAM_PERIOD)) {
}

void Krb5CCacheStore::ServiceData::bumpCount() {
  folly::SharedMutex::WriteHolder guard(lockTimeSeries);
  time_t now = time(nullptr);
  timeSeries.addValue(std::chrono::seconds(now), 1);
}

uint64_t Krb5CCacheStore::ServiceData::getCount() {
  folly::SharedMutex::ReadHolder guard(lockTimeSeries);
  // Note that we don't have a need to call timeSeries_.update(<time>)
  // here because we don't care to have the exact count at the current
  // time. We're ok with grabbing the count at the last update.
  return timeSeries.count();
}

std::shared_ptr<Krb5CCache> Krb5CCacheStore::waitForCache(
    const Krb5Principal& service,
    SecurityLogger* logger) {
  std::shared_ptr<ServiceData> dataPtr = getServiceDataPtr(service);

  // Bump that we've used the principal
  dataPtr->bumpCount();

  if (logger) {
    logger->logStart("get_prepared_cache", folly::to<string>(service));
  }

  {
    folly::SharedMutex::ReadHolder readLock(dataPtr->lockCache);
    // If there is a cache, just return it. Try with a read lock first
    // for performance reasons.
    if (dataPtr->cache) {
      if (logger) {
        logger->logEnd("get_prepared_cache");
      }
      return dataPtr->cache;
    }
  }

  // If we didn't find a cache, initialize a local, temporary cache for the
  // service. We might throw this away if some other thread initialized the
  // cache meanwhile.
  if (logger) {
    logger->logStart("init_cache_for_service", folly::to<string>(service));
  }
  auto tempCache = initCacheForService(service, nullptr, logger);
  if (logger) {
    logger->logEnd("init_cache_for_service");
  }

  // Upgrade to a write lock, and initialize the cache only if it is not already
  // initialized by some other thread meanwhile.
  folly::SharedMutex::WriteHolder writeLock(dataPtr->lockCache);
  if (!dataPtr->cache) {
    dataPtr->cache = std::move(tempCache);
  }
  return dataPtr->cache;
}

std::shared_ptr<Krb5CCacheStore::ServiceData>
    Krb5CCacheStore::getServiceDataPtr(const Krb5Principal& service) {

  // If ccache is disabled, just return a fresh ServiceData obj
  if (maxCacheSize_ == 0) {
    return std::make_shared<ServiceData>();
  }

  string service_name = folly::to<string>(service);
  {
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    auto found = serviceDataMap_.find(service_name);
    if (found != serviceDataMap_.end()) {
      // Get the element from the map if it exists
      return found->second;
    }
  }

  // Not found, we need to create it
  folly::SharedMutex::WriteHolder writeLock(serviceDataMapLock_);
  auto found = serviceDataMap_.find(service_name);
  if (found == serviceDataMap_.end()) {
    // If we reached the limit, then we need to free some room
    if (maxCacheSize_ > 0 && cacheItemQueue_.size() >= maxCacheSize_) {
      serviceDataMap_.erase(cacheItemQueue_.front());
      cacheItemQueue_.pop();
    }
    serviceDataMap_[service_name] = std::make_shared<ServiceData>();
    cacheItemQueue_.push(service_name);
  }
  return serviceDataMap_[service_name];
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::initCacheForService(
    const Krb5Principal& service,
    const krb5_creds* creds,
    SecurityLogger* logger) {

  if (logger) {
    logger->logStart("wait_for_tgt");
  }
  std::shared_ptr<const Krb5Credentials> tgt;
  auto client_princ = tgts_.getClientPrincipal();
  // Get a cross-realm tgt if we need
  string srealm = service.getRealm();
  if (srealm != tgts_.getClientPrincipal().getRealm()) {
    tgt = tgts_.getTgtForRealm(srealm);
  } else {
    tgt = tgts_.getTgt();
  }
  if (logger) {
    logger->logEnd("wait_for_tgt");
  }

  if (logger) {
    logger->logStart("init_barebones_ccache");
  }
  // Make a new memory cache.
  auto mem = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  mem->setDestroyOnClose();
  mem->initialize(client_princ.get());
  mem->storeCred(tgt->get());
  if (logger) {
    logger->logEnd("init_barebones_ccache");
  }

  if (creds != nullptr) {
    mem->storeCred(*creds);
  } else {
    if (logger) {
      logger->logStart("get_credential_from_kdc");
    }
    mem->getCredentials(service.get());
    if (logger) {
      logger->logEnd("get_credential_from_kdc");
    }
  }

  return mem;
}

void Krb5CCacheStore::importCache(
    Krb5CCache& file_cache) {
  logger_->logStart("import_tgts");
  // Split out tgts and service principals
  std::vector<Krb5Credentials> tgts;
  std::vector<Krb5Credentials> services;
  uint64_t count = 0;
  for (auto& tmp_cred : file_cache) {
    Krb5Credentials cred(std::move(tmp_cred));
    Krb5Principal server = Krb5Principal::copyPrincipal(
      ctx_.get(), cred.get().server);
    if (server.isTgt()) {
      tgts.push_back(std::move(cred));
      count++;
    } else if (!server.getRealm().empty()) {
      // Sometimes, somehow, principals for host/{hg.vvv,svn.vip} have no
      // realm!!  Why?  No idea, but they exist and they blow up the entire
      // cache reading thread, so defend against them for now by ignoring that
      // entry (example: #7808411).
      services.push_back(std::move(cred));
    }
  }

  // Import TGTs
  Krb5Tgts tgts_obj;
  Krb5Principal client_principal = file_cache.getClientPrincipal();
  tgts_obj.setClientPrincipal(client_principal);
  for (auto& tgt : tgts) {
    Krb5Principal server_principal = Krb5Principal::copyPrincipal(
      ctx_.get(), tgt.get().server);
    if (server_principal.getComponent(1) == client_principal.getRealm()) {
      tgts_obj.setTgt(folly::make_unique<Krb5Credentials>(std::move(tgt)));
    } else {
      tgts_obj.setTgtForRealm(
        server_principal.getComponent(1),
        folly::make_unique<Krb5Credentials>(std::move(tgt)));
    }
  }
  tgts_ = std::move(tgts_obj);
  logger_->logEnd("import_tgts", folly::to<std::string>(count));

  // Import service creds
  DataMapType new_data_map;
  std::queue<std::string> new_data_queue;
  logger_->logStart("import_service_creds");
  count = 0;
  for (auto& service : services) {
    if (maxCacheSize_ >= 0 && count >= maxCacheSize_) {
      break;
    }
    Krb5Principal princ = Krb5Principal::copyPrincipal(
      ctx_.get(), service.get().server);
    auto mem = initCacheForService(princ, &service.get());

    auto data = std::make_shared<ServiceData>();
    std::string name = folly::to<string>(princ);
    new_data_map[name] = data;
    new_data_queue.push(name);
    data->cache = std::move(mem);
    data->bumpCount();
    count++;
  }

  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_ = std::move(new_data_map);
  cacheItemQueue_ = std::move(new_data_queue);
  logger_->logEnd("import_service_creds", folly::to<std::string>(count));
}

std::vector<Krb5Principal> Krb5CCacheStore::getServicePrincipalList() {
  std::vector<Krb5Principal> services;
  folly::SharedMutex::ReadHolder lock(serviceDataMapLock_);
  for (auto& data : serviceDataMap_) {
    folly::SharedMutex::ReadHolder lock(data.second->lockCache);
    if (!data.second->cache) {
      continue;
    }
    auto princ_list = data.second->cache->getServicePrincipalList();
    if (princ_list.size() < 1) {
      throw std::runtime_error("Principal list too small in ccache");
    }
    auto princ = std::move(princ_list[0]);
    services.push_back(std::move(princ));
  }
  return services;
}

uint64_t Krb5CCacheStore::renewCreds() {
  auto curTgt = tgts_.getTgt();
  auto client_princ = tgts_.getClientPrincipal();
  auto realms = tgts_.getValidRealms();
  uint64_t renewCount = 0;

  // Renew TGTs
  Krb5Tgts tgts;
  tgts.kInit(client_princ);
  for (const auto& realm : realms) {
    tgts.getTgtForRealm(realm);
  }
  tgts_ = std::move(tgts);

  // Renew service creds, and store the renewed creds in a temporary map
  std::unordered_map<string, std::unique_ptr<Krb5CCache>> renewed_map;
  for (auto& service : getServicePrincipalList()) {
    try {
      auto mem = initCacheForService(service);
      renewed_map[folly::to<string>(service)] = std::move(mem);
      renewCount++;
    } catch (const std::runtime_error& e) {
      VLOG(4) << "Failed to renew cred for service: "
              << folly::to<string>(service) << " "
              << e.what();
    }
  }

  // Update the main service data map with the renewed creds.
  // If the creds failed to be renewed and the old ones are stale, then
  // just delete the old ones.
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  for (auto& entry : serviceDataMap_) {
    auto renewed_entry = renewed_map.find(entry.first);
    folly::SharedMutex::WriteHolder lock(entry.second->lockCache);
    if (renewed_entry != renewed_map.end()) {
      entry.second->cache = std::move(renewed_entry->second);
    } else if (entry.second->cache) {
      // Not found, see if it's a new cred or old one
      auto lifetime = entry.second->cache->getLifetime();
      auto tgt_lifetime = tgts_.getLifetime();
      if (lifetime.second < tgt_lifetime.second) {
        // Delete old credential
        entry.second->cache.reset();
      }
    } // else
    // It's possible this thread wins the race to hit a service in the
    // data map that hasn't yet obtained a ccache. In this case, just leave
    // it alone. It will get the correct ccache.
  }
  return renewCount;
}

std::unique_ptr<Krb5CCache> Krb5CCacheStore::exportCache(size_t limit) {
  Krb5Principal client_principal = tgts_.getClientPrincipal();

  // Make a new memory cache.
  auto temp_cache = folly::make_unique<Krb5CCache>(
    Krb5CCache::makeNewUnique("MEMORY"));
  // Initialize the new CC
  temp_cache->initialize(client_principal.get());

  {
    // Put 'limit' number of most frequently used credentials into the
    // top_services set.
    folly::SharedMutex::ReadHolder readLock(serviceDataMapLock_);
    vector<pair<string, uint64_t>> count_vector;
    for (auto& element : serviceDataMap_) {
      count_vector.emplace_back(element.first, element.second->getCount());
    }

    sort(count_vector.begin(), count_vector.end(), serviceCountCompare);

    std::set<string> top_services;
    int count = 0;
    for (auto& element : count_vector) {
      if (count >= limit) {
        break;
      }
      top_services.insert(element.first);
      count++;
    }

    for (auto& data : serviceDataMap_) {
      folly::SharedMutex::ReadHolder lock(data.second->lockCache);
      if (!data.second->cache) {
        continue;
      }
      auto princ_list = data.second->cache->getServicePrincipalList();
      if (princ_list.size() < 1) {
        throw std::runtime_error("Principal list too small in ccache");
      }
      auto princ = std::move(princ_list[0]);
      if (top_services.count(folly::to<string>(princ)) == 0) {
        continue;
      }
      Krb5Credentials service_cred = data.second->cache->retrieveCred(
        princ.get());
      temp_cache->storeCred(service_cred.get());
    }
  }

  // Store the TGTs in the cache.
  temp_cache->storeCred(tgts_.getTgt()->get());
  auto realms = tgts_.getValidRealms();
  for (const auto& realm : realms) {
    auto cred = tgts_.getTgtForRealm(realm);
    temp_cache->storeCred(cred->get());
  }
  return temp_cache;
}

bool Krb5CCacheStore::isInitialized() {
  return tgts_.isInitialized();
}

std::pair<uint64_t, uint64_t> Krb5CCacheStore::getLifetime() {
  return tgts_.getLifetime();
}

Krb5Principal Krb5CCacheStore::getClientPrincipal() {
  return tgts_.getClientPrincipal();
}

void Krb5CCacheStore::kInit(const Krb5Principal& client) {
  tgts_.kInit(client);
  folly::SharedMutex::WriteHolder service_data_lock(serviceDataMapLock_);
  serviceDataMap_.clear();
  std::queue<std::string> emptyQueue;
  cacheItemQueue_ = std::move(emptyQueue);
}

void Krb5CCacheStore::notifyOfError(const std::string& error) {
  tgts_.notifyOfError(error);
}

}}}
<|endoftext|>


<commit_before>#include "myofferwhitelisttablemodel.h"

#include "guiutil.h"
#include "walletmodel.h"

#include "wallet/wallet.h"
#include "base58.h"

#include <QFont>
using namespace std;

struct MyOfferWhitelistTableEntry
{
	QString offer;
    QString alias;
	QString expires;
	QString discount;

    MyOfferWhitelistTableEntry() {}
    MyOfferWhitelistTableEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount):
        offer(offer), alias(alias), expires(expires),discount(discount) {}
};

// Private implementation
class MyOfferWhitelistTablePriv
{
public:
    QList<MyOfferWhitelistTableEntry> cachedEntryTable;
    MyOfferWhitelistTableModel *parent;

    OfferWhitelistTablePriv(MyOfferWhitelistTableModel *parent):
        parent(parent) {}

    void updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
    {
		if(!parent)
		{
			return;
		}

        switch(status)
        {
        case CT_NEW:

            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedEntryTable.insert(lowerIndex, MyOfferWhitelistTableEntry(offer, alias, expires, discount));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            lower->offer = offer;
			lower->alias = alias;
			lower->expires = expires;
			lower->discount = discount;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:

            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedEntryTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedEntryTable.size();
    }

    OfferWhitelistTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedEntryTable.size())
        {
            return &cachedEntryTable[idx];
        }
        else
        {
            return 0;
        }
    }
};

MyOfferWhitelistTableModel::MyOfferWhitelistTableModel(WalletModel *parent) :
    QAbstractTableModel(parent)
{
    columns << tr("Offer") << tr("Alias") << tr("Discount") << tr("Expires In");
    priv = new OfferWhitelistTablePriv(this);

}

MyOfferWhitelistTableModel::~MyOfferWhitelistTableModel()
{
    delete priv;
}
int MyOfferWhitelistTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MyOfferWhitelistTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MyOfferWhitelistTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    OfferWhitelistTableEntry *rec = static_cast<OfferWhitelistTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            return rec->offer;
        case Alias:
            return rec->alias;
        case Discount:
            return rec->discount;
        case Expires:
            return rec->expires;
        }
    }
    else if (role == AliasRole)
    {
        return rec->alias;
    }
    return QVariant();
}

bool MyOfferWhitelistTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    OfferWhitelistTableEntry *rec = static_cast<OfferWhitelistTableEntry*>(index.internalPointer());

    editStatus = OK;

    if(role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            // Do nothing, if old value == new value
            if(rec->offer == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Alias:
            // Do nothing, if old value == new value
            if(rec->alias == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Discount:
            // Do nothing, if old value == new value
            if(rec->discount == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
       case Expires:
            // Do nothing, if old value == new value
            if(rec->expires == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            break;
        return true;
		}
    }
    return false;
}

QVariant MyOfferWhitelistTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags MyOfferWhitelistTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;
    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    return retval;
}

QModelIndex MyOfferWhitelistTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    OfferWhitelistTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MyOfferWhitelistTableModel::updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
{
    priv->updateEntry(offer, alias, expires, discount, status);
}

QString MyOfferWhitelistTableModel::addRow(const QString &offer, const QString &alias, const QString &expires,const QString &discount)
{
    std::string strAlias = alias.toStdString();
    editStatus = OK;
    return QString::fromStdString(strAlias);
}
void MyOfferWhitelistTableModel::clear()
{
	beginResetModel();
    priv->cachedEntryTable.clear();
	endResetModel();
}

void MyOfferWhitelistTableModel::emitDataChanged(int idx)
{
    Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
<commit_msg>compile<commit_after>#include "myofferwhitelisttablemodel.h"

#include "guiutil.h"
#include "walletmodel.h"

#include "wallet/wallet.h"
#include "base58.h"

#include <QFont>
using namespace std;

struct MyOfferWhitelistTableEntry
{
	QString offer;
    QString alias;
	QString expires;
	QString discount;

    MyOfferWhitelistTableEntry() {}
    MyOfferWhitelistTableEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount):
        offer(offer), alias(alias), expires(expires),discount(discount) {}
};

// Private implementation
class MyOfferWhitelistTablePriv
{
public:
    QList<MyOfferWhitelistTableEntry> cachedEntryTable;
    MyOfferWhitelistTableModel *parent;

    MyOfferWhitelistTablePriv(MyOfferWhitelistTableModel *parent):
        parent(parent) {}

    void updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
    {
		if(!parent)
		{
			return;
		}

        switch(status)
        {
        case CT_NEW:

            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedEntryTable.insert(lowerIndex, MyOfferWhitelistTableEntry(offer, alias, expires, discount));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            lower->offer = offer;
			lower->alias = alias;
			lower->expires = expires;
			lower->discount = discount;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:

            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedEntryTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedEntryTable.size();
    }

    MyOfferWhitelistTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedEntryTable.size())
        {
            return &cachedEntryTable[idx];
        }
        else
        {
            return 0;
        }
    }
};

MyOfferWhitelistTableModel::MyOfferWhitelistTableModel(WalletModel *parent) :
    QAbstractTableModel(parent)
{
    columns << tr("Offer") << tr("Alias") << tr("Discount") << tr("Expires In");
    priv = new OfferWhitelistTablePriv(this);

}

MyOfferWhitelistTableModel::~MyOfferWhitelistTableModel()
{
    delete priv;
}
int MyOfferWhitelistTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int MyOfferWhitelistTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant MyOfferWhitelistTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    MyOfferWhitelistTableEntry *rec = static_cast<MyOfferWhitelistTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            return rec->offer;
        case Alias:
            return rec->alias;
        case Discount:
            return rec->discount;
        case Expires:
            return rec->expires;
        }
    }
    else if (role == AliasRole)
    {
        return rec->alias;
    }
    return QVariant();
}

bool MyOfferWhitelistTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    MyOfferWhitelistTableEntry *rec = static_cast<MyOfferWhitelistTableEntry*>(index.internalPointer());

    editStatus = OK;

    if(role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Offer:
            // Do nothing, if old value == new value
            if(rec->offer == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Alias:
            // Do nothing, if old value == new value
            if(rec->alias == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
        case Discount:
            // Do nothing, if old value == new value
            if(rec->discount == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }

            break;
       case Expires:
            // Do nothing, if old value == new value
            if(rec->expires == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            break;
        return true;
		}
    }
    return false;
}

QVariant MyOfferWhitelistTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags MyOfferWhitelistTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;
    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    return retval;
}

QModelIndex MyOfferWhitelistTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    MyOfferWhitelistTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void MyOfferWhitelistTableModel::updateEntry(const QString &offer, const QString &alias, const QString &expires,const QString &discount, int status)
{
    priv->updateEntry(offer, alias, expires, discount, status);
}

QString MyOfferWhitelistTableModel::addRow(const QString &offer, const QString &alias, const QString &expires,const QString &discount)
{
    std::string strAlias = alias.toStdString();
    editStatus = OK;
    return QString::fromStdString(strAlias);
}
void MyOfferWhitelistTableModel::clear()
{
	beginResetModel();
    priv->cachedEntryTable.clear();
	endResetModel();
}

void MyOfferWhitelistTableModel::emitDataChanged(int idx)
{
    Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
<|endoftext|>


<commit_before>//(c) 2016 by Authors
//This file is a part of ABruijn program.
//Released under the BSD license (see LICENSE file)

#include <deque>

#include "graph_processing.h"
#include "../common/logger.h"
#include "../common/config.h"
#include "../common/utils.h"

//a helper function that calls
//all other simplification procedures
void GraphProcessor::simplify()
{
	this->trimTips();
	this->condenceEdges();
	this->fixChimericJunctions();
	this->collapseBulges();
	this->condenceEdges();
	this->trimTips();
}

//finds and removes graph structures that
//originate from the typical chimeric read pattern
//(when a read contain two consecivtive reversed copies of the real sequence)
void GraphProcessor::fixChimericJunctions()
{
	//a very specific case: 1 in - 1 out
	std::unordered_set<GraphNode*> simpleCases;
	for (auto& node : _graph.iterNodes())
	{
		if (!node->isBifurcation() &&
			(node->inEdges.front()->edgeId ==
			 node->outEdges.front()->edgeId.rc()))
		{
			simpleCases.insert(node);
		}
	}
	for (auto& node : simpleCases)
	{
		GraphNode* newNode = _graph.addNode();
		GraphEdge* cutEdge = node->outEdges.front();
		newNode->outEdges.push_back(cutEdge);
		cutEdge->nodeLeft = newNode;
		node->outEdges.clear();
	}

	//more common case: 2 in - 2 out
	std::unordered_set<GraphNode*> complexCases;
	for (auto& node : _graph.iterNodes())
	{
		if (node->inEdges.size() != 2 ||
			node->outEdges.size() != 2) continue;
		auto& inEdges = node->inEdges;
		auto& outEdges = node->outEdges;
		if (inEdges[0]->edgeId.rc() != outEdges[0]->edgeId)
		{
			//match INs with OUTs
			std::swap(inEdges[0], inEdges[1]);
		}

		if (inEdges[0]->edgeId.rc() == outEdges[0]->edgeId &&
			inEdges[1]->edgeId.rc() == outEdges[1]->edgeId)
		{
			complexCases.insert(node);
		}
	}
	for(auto& node : complexCases)
	{
		GraphNode* newNode = _graph.addNode();
		node->inEdges[1]->nodeRight = newNode;
		node->outEdges[0]->nodeLeft = newNode;
		newNode->inEdges.push_back(node->inEdges[1]);
		newNode->outEdges.push_back(node->outEdges[0]);

		node->inEdges.pop_back();
		node->outEdges.erase(node->outEdges.begin());
	}

	Logger::get().debug() << "Removed "
		<< simpleCases.size() + complexCases.size()
		<< " chimeric junctions";
}

//Collapses simple small bulges
void GraphProcessor::collapseBulges()
{
	const int MAX_BUBBLE = Parameters::get().minimumOverlap;
	std::unordered_set<std::pair<GraphNode*, GraphNode*>,
					   pairhash> toFix;
	//finding the bubbles
	for (auto& edge : _graph.iterEdges())
	{
		if (edge->isLooped()) continue;
		std::vector<GraphEdge*> parallelEdges;
		for (auto& parEdge : edge->nodeLeft->outEdges)
		{
			if (parEdge->nodeRight == edge->nodeRight)
			{
				parallelEdges.push_back(parEdge);
			}
		}
		if (parallelEdges.size() != 2) continue;
		if (parallelEdges[0]->edgeId == parallelEdges[1]->edgeId.rc()) continue;
		if (parallelEdges[0]->length() > MAX_BUBBLE ||
			parallelEdges[1]->length() > MAX_BUBBLE) continue;

		toFix.emplace(edge->nodeLeft, edge->nodeRight);
	}

	//collapsing them by leaving only one branch and
	//adding the other's sequences as alternative to the
	//remaining branch
	for (auto& nodes : toFix)
	{
		std::vector<GraphEdge*> parallelEdges;
		for (auto& parEdge : nodes.first->outEdges)
		{
			if (parEdge->nodeRight == nodes.second)
			{
				parallelEdges.push_back(parEdge);
			}
		}
		GraphEdge* edgeOne = parallelEdges[0];
		GraphEdge* edgeTwo = parallelEdges[1];
		if (abs(edgeOne->edgeId.signedId()) > abs(edgeTwo->edgeId.signedId()))
		{
			std::swap(edgeOne, edgeTwo);
		}
		for (auto& seg : edgeTwo->seqSegments)
		{
			edgeOne->seqSegments.push_back(seg);
		}
		_graph.removeEdge(edgeTwo);
	}
	Logger::get().debug() << "Collapsed " << toFix.size() / 2 << " bulges";
}

//Removing tips
void GraphProcessor::trimTips()
{
	const int TIP_THRESHOLD = Config::get("tip_length_threshold");
	std::unordered_set<GraphEdge*> toRemove;
	for (GraphEdge* tipEdge : _graph.iterEdges())
	{
		int rightOut = tipEdge->nodeRight->outEdges.size();
		int leftIn = tipEdge->nodeLeft->inEdges.size();
		int leftOut = tipEdge->nodeLeft->outEdges.size();

		if (tipEdge->length() < TIP_THRESHOLD && rightOut == 0 &&
			leftIn == 1 && leftOut == 1)
		{
			toRemove.insert(tipEdge);
			toRemove.insert(_graph.complementEdge(tipEdge));
		}
	}

	Logger::get().debug() << toRemove.size() / 2 << " tips removed";
	for (auto& edge : toRemove)	_graph.removeEdge(edge);
}

//This function collapses non-branching edges paths in the graph.
//The tricky part is the sequence representation of the new edges.
//Two (or more) consecutive egdes will only be collapsed into one
//if there exist at least one consigous sub-sequence from the input assembly
//that can represent this edge
void GraphProcessor::condenceEdges()
{
	int edgesRemoved = 0;
	int edgesAdded = 0;

	//helper function that checks if the simplification is
	//possible and returns the new edges that should replace
	//the original ones
	auto collapseEdges = [] (const GraphPath& edges)
	{
		std::vector<GraphEdge> newEdges;
		std::list<SequenceSegment> growingSeqs(edges.front()->seqSegments.begin(),
											   edges.front()->seqSegments.end());
		assert(edges.size() > 1);
		size_t prevStart = 0;
		for (size_t i = 1; i < edges.size(); ++i)
		{
			auto prevSeqs = growingSeqs;
			for (auto prevSeg = growingSeqs.begin();
				 prevSeg != growingSeqs.end(); )
			{
				bool continued = false;
				for (auto& nextSeg : edges[i]->seqSegments)
				{
					if (prevSeg->seqId == nextSeg.seqId &&
						prevSeg->end == nextSeg.start)
					{
						continued = true;
						prevSeg->end = nextSeg.end;
					}
				}
				if (!continued)
				{
					prevSeg = growingSeqs.erase(prevSeg);
				}
				else
				{
					++prevSeg;
				}
			}

			if (growingSeqs.empty())
			{
				newEdges.emplace_back(edges[prevStart]->nodeLeft,
									  edges[i - 1]->nodeRight);
				std::copy(prevSeqs.begin(), prevSeqs.end(),
				  		  std::back_inserter(newEdges.back().seqSegments));

				std::copy(edges[i]->seqSegments.begin(),
						  edges[i]->seqSegments.end(),
						  std::back_inserter(growingSeqs));
				prevStart = i;
			}
		}

		newEdges.emplace_back(edges[prevStart]->nodeLeft,
							  edges.back()->nodeRight);
		std::copy(growingSeqs.begin(), growingSeqs.end(),
				  std::back_inserter(newEdges.back().seqSegments));

		return newEdges;
	};

	//Try to collapse each unbranching path.
	//If it is possible - replace with new edges
	auto toCollapse = this->getUnbranchingPaths();
	for (auto& unbranchingPath : toCollapse)
	{
		if (!unbranchingPath.id.strand()) continue;

		GraphPath complPath = _graph.complementPath(unbranchingPath.path);
		auto newEdges = collapseEdges(unbranchingPath.path);
		if (newEdges.size() == unbranchingPath.path.size()) continue;

		std::string addedStr;
		for (auto& edge : newEdges)
		{
			//do not add collapsed short loops
			//if (edge.length() < Parameters::get().minimumOverlap &&
			//	edge.isLooped()) continue;

			GraphEdge addFwd = edge;
			addFwd.edgeId = _graph.newEdgeId();

			GraphEdge addRev(_graph.complementNode(edge.nodeRight),
							 _graph.complementNode(edge.nodeLeft),
							 addFwd.edgeId.rc());

			//complementary segments
			for (auto seqSeg : addFwd.seqSegments)
			{
				addRev.seqSegments.push_back(seqSeg.complement());
			}

			GraphEdge* addedEdge = _graph.addEdge(std::move(addFwd));
			_graph.addEdge(std::move(addRev));

			addedStr += std::to_string(addedEdge->edgeId.signedId()) + " -> ";
		}

		if (addedStr.size() > 4) addedStr.erase(addedStr.size() - 4);
		Logger::get().debug() << "Collapsed: " << unbranchingPath.edgesStr()
			<< " to " << addedStr;

		std::unordered_set<GraphEdge*> toRemove;
		for (auto& edge : unbranchingPath.path) toRemove.insert(edge);
		for (auto& edge : complPath) toRemove.insert(edge);
		for (auto& edge : toRemove) _graph.removeEdge(edge);

		edgesRemoved += unbranchingPath.path.size();
		edgesAdded += newEdges.size();
	}

	Logger::get().debug() << "Removed " << edgesRemoved << " edges";
	Logger::get().debug() << "Added " << edgesAdded << " edges";
}

//converts edges to unbranching paths
std::vector<UnbranchingPath> GraphProcessor::getEdgesPaths() const
{
	std::vector<UnbranchingPath> paths;
	for (auto& edge : _graph.iterEdges())
	{
		GraphPath path = {edge};
		paths.emplace_back(path, edge->edgeId, false,
						   edge->length(), edge->meanCoverage);
		paths.back().repetitive = edge->repetitive;
	}
	return paths;
}

//Finds unbranching paths
std::vector<UnbranchingPath> GraphProcessor::getUnbranchingPaths() const
{
	std::unordered_map<FastaRecord::Id, size_t> edgeIds;
	size_t nextEdgeId = 0;
	auto pathToId = [&edgeIds, &nextEdgeId](GraphPath path)
	{
		if (!edgeIds.count(path.front()->edgeId))
		{
			for (auto edge : path)
			{
				edgeIds[edge->edgeId] = nextEdgeId;
				edgeIds[edge->edgeId.rc()] = nextEdgeId + 1;
			}
			nextEdgeId += 2;
			return FastaRecord::Id(nextEdgeId - 2);
		}
		return FastaRecord::Id(edgeIds[path.front()->edgeId]);
	};

	std::vector<UnbranchingPath> unbranchingPaths;
	std::unordered_set<GraphEdge*> visitedEdges;
	for (auto edge : _graph.iterEdges())
	{
		if (visitedEdges.count(edge)) continue;
		visitedEdges.insert(edge);

		GraphPath traversed;
		traversed.push_back(edge);
		if (!edge->selfComplement)
		{
			GraphNode* curNode = edge->nodeLeft;
			while (!curNode->isBifurcation() &&
				   !curNode->inEdges.empty() &&
				   !visitedEdges.count(curNode->inEdges.front()) &&
				   !curNode->inEdges.front()->selfComplement)
			{
				traversed.push_back(curNode->inEdges.front());
				visitedEdges.insert(traversed.back());
				curNode = curNode->inEdges.front()->nodeLeft;
			}
			std::reverse(traversed.begin(), traversed.end());
			curNode = edge->nodeRight;
			while (!curNode->isBifurcation() &&
				   !curNode->outEdges.empty() &&
				   !visitedEdges.count(curNode->outEdges.front()) &&
				   !curNode->outEdges.front()->selfComplement)
			{
				traversed.push_back(curNode->outEdges.front());
				visitedEdges.insert(traversed.back());
				curNode = curNode->outEdges.front()->nodeRight;
			}
		}

		FastaRecord::Id edgeId = pathToId(traversed);
		bool circular = (traversed.front()->nodeLeft ==
							traversed.back()->nodeRight) &&
						traversed.front()->nodeLeft->outEdges.size() == 1 &&
						traversed.front()->nodeLeft->inEdges.size() == 1;

		bool repetitive = traversed.front()->isRepetitive() ||
						  traversed.back()->isRepetitive();

		int contigLength = 0;
		int64_t sumCov = 0;
		for (auto& edge : traversed)
		{
			contigLength += edge->length();
			sumCov += edge->meanCoverage * edge->length();
		}
		int meanCoverage = contigLength ? sumCov / contigLength : 0;

		unbranchingPaths.emplace_back(traversed, edgeId, circular,
							  contigLength, meanCoverage);
		unbranchingPaths.back().repetitive = repetitive;
	}
	return unbranchingPaths;
}
<commit_msg>one cannot simply implement tip clipping<commit_after>//(c) 2016 by Authors
//This file is a part of ABruijn program.
//Released under the BSD license (see LICENSE file)

#include <deque>

#include "graph_processing.h"
#include "../common/logger.h"
#include "../common/config.h"
#include "../common/utils.h"

//a helper function that calls
//all other simplification procedures
void GraphProcessor::simplify()
{
	this->trimTips();
	this->condenceEdges();
	this->fixChimericJunctions();
	this->collapseBulges();
	this->condenceEdges();
	this->trimTips();
}

//finds and removes graph structures that
//originate from the typical chimeric read pattern
//(when a read contain two consecivtive reversed copies of the real sequence)
void GraphProcessor::fixChimericJunctions()
{
	//a very specific case: 1 in - 1 out
	std::unordered_set<GraphNode*> simpleCases;
	for (auto& node : _graph.iterNodes())
	{
		if (!node->isBifurcation() &&
			(node->inEdges.front()->edgeId ==
			 node->outEdges.front()->edgeId.rc()))
		{
			simpleCases.insert(node);
		}
	}
	for (auto& node : simpleCases)
	{
		GraphNode* newNode = _graph.addNode();
		GraphEdge* cutEdge = node->outEdges.front();
		newNode->outEdges.push_back(cutEdge);
		cutEdge->nodeLeft = newNode;
		node->outEdges.clear();
	}

	//more common case: 2 in - 2 out
	std::unordered_set<GraphNode*> complexCases;
	for (auto& node : _graph.iterNodes())
	{
		if (node->inEdges.size() != 2 ||
			node->outEdges.size() != 2) continue;
		auto& inEdges = node->inEdges;
		auto& outEdges = node->outEdges;
		if (inEdges[0]->edgeId.rc() != outEdges[0]->edgeId)
		{
			//match INs with OUTs
			std::swap(inEdges[0], inEdges[1]);
		}

		if (inEdges[0]->edgeId.rc() == outEdges[0]->edgeId &&
			inEdges[1]->edgeId.rc() == outEdges[1]->edgeId)
		{
			complexCases.insert(node);
		}
	}
	for(auto& node : complexCases)
	{
		GraphNode* newNode = _graph.addNode();
		node->inEdges[1]->nodeRight = newNode;
		node->outEdges[0]->nodeLeft = newNode;
		newNode->inEdges.push_back(node->inEdges[1]);
		newNode->outEdges.push_back(node->outEdges[0]);

		node->inEdges.pop_back();
		node->outEdges.erase(node->outEdges.begin());
	}

	Logger::get().debug() << "Removed "
		<< simpleCases.size() + complexCases.size()
		<< " chimeric junctions";
}

//Collapses simple small bulges
void GraphProcessor::collapseBulges()
{
	const int MAX_BUBBLE = Parameters::get().minimumOverlap;
	std::unordered_set<std::pair<GraphNode*, GraphNode*>,
					   pairhash> toFix;
	//finding the bubbles
	for (auto& edge : _graph.iterEdges())
	{
		if (edge->isLooped()) continue;
		std::vector<GraphEdge*> parallelEdges;
		for (auto& parEdge : edge->nodeLeft->outEdges)
		{
			if (parEdge->nodeRight == edge->nodeRight)
			{
				parallelEdges.push_back(parEdge);
			}
		}
		if (parallelEdges.size() != 2) continue;
		if (parallelEdges[0]->edgeId == parallelEdges[1]->edgeId.rc()) continue;
		if (parallelEdges[0]->length() > MAX_BUBBLE ||
			parallelEdges[1]->length() > MAX_BUBBLE) continue;

		toFix.emplace(edge->nodeLeft, edge->nodeRight);
	}

	//collapsing them by leaving only one branch and
	//adding the other's sequences as alternative to the
	//remaining branch
	for (auto& nodes : toFix)
	{
		std::vector<GraphEdge*> parallelEdges;
		for (auto& parEdge : nodes.first->outEdges)
		{
			if (parEdge->nodeRight == nodes.second)
			{
				parallelEdges.push_back(parEdge);
			}
		}
		GraphEdge* edgeOne = parallelEdges[0];
		GraphEdge* edgeTwo = parallelEdges[1];
		if (abs(edgeOne->edgeId.signedId()) > abs(edgeTwo->edgeId.signedId()))
		{
			std::swap(edgeOne, edgeTwo);
		}
		for (auto& seg : edgeTwo->seqSegments)
		{
			edgeOne->seqSegments.push_back(seg);
		}
		_graph.removeEdge(edgeTwo);
	}
	Logger::get().debug() << "Collapsed " << toFix.size() / 2 << " bulges";
}

//Removing tips
void GraphProcessor::trimTips()
{
	const int TIP_THRESHOLD = Config::get("tip_length_threshold");
	std::unordered_set<GraphEdge*> toRemove;
	for (GraphEdge* tipEdge : _graph.iterEdges())
	{
		int rightOut = tipEdge->nodeRight->outEdges.size();
		int leftIn = tipEdge->nodeLeft->inEdges.size();
		int leftOut = tipEdge->nodeLeft->outEdges.size();

		if (tipEdge->length() < TIP_THRESHOLD && rightOut == 0 &&
			leftIn == 1 && leftOut == 2 &&
			!tipEdge->nodeLeft->inEdges.front()->isLooped())
		{
			toRemove.insert(tipEdge);
		}
	}

	for (auto& edge : toRemove)
	{
		vecRemove(edge->nodeLeft->outEdges, edge);
		edge->nodeLeft = _graph.addNode();
		edge->nodeLeft->outEdges.push_back(edge);

		GraphEdge* complEdge = _graph.complementEdge(edge);
		vecRemove(complEdge->nodeRight->inEdges, complEdge);
		complEdge->nodeRight = _graph.addNode();
		complEdge->nodeRight->inEdges.push_back(complEdge);
	}
	Logger::get().debug() << toRemove.size() << " tips clipped";
}

//This function collapses non-branching edges paths in the graph.
//The tricky part is the sequence representation of the new edges.
//Two (or more) consecutive egdes will only be collapsed into one
//if there exist at least one consigous sub-sequence from the input assembly
//that can represent this edge
void GraphProcessor::condenceEdges()
{
	int edgesRemoved = 0;
	int edgesAdded = 0;

	//helper function that checks if the simplification is
	//possible and returns the new edges that should replace
	//the original ones
	auto collapseEdges = [] (const GraphPath& edges)
	{
		std::vector<GraphEdge> newEdges;
		std::list<SequenceSegment> growingSeqs(edges.front()->seqSegments.begin(),
											   edges.front()->seqSegments.end());
		assert(edges.size() > 1);
		size_t prevStart = 0;
		for (size_t i = 1; i < edges.size(); ++i)
		{
			auto prevSeqs = growingSeqs;
			for (auto prevSeg = growingSeqs.begin();
				 prevSeg != growingSeqs.end(); )
			{
				bool continued = false;
				for (auto& nextSeg : edges[i]->seqSegments)
				{
					if (prevSeg->seqId == nextSeg.seqId &&
						prevSeg->end == nextSeg.start)
					{
						continued = true;
						prevSeg->end = nextSeg.end;
					}
				}
				if (!continued)
				{
					prevSeg = growingSeqs.erase(prevSeg);
				}
				else
				{
					++prevSeg;
				}
			}

			if (growingSeqs.empty())
			{
				newEdges.emplace_back(edges[prevStart]->nodeLeft,
									  edges[i - 1]->nodeRight);
				std::copy(prevSeqs.begin(), prevSeqs.end(),
				  		  std::back_inserter(newEdges.back().seqSegments));

				std::copy(edges[i]->seqSegments.begin(),
						  edges[i]->seqSegments.end(),
						  std::back_inserter(growingSeqs));
				prevStart = i;
			}
		}

		newEdges.emplace_back(edges[prevStart]->nodeLeft,
							  edges.back()->nodeRight);
		std::copy(growingSeqs.begin(), growingSeqs.end(),
				  std::back_inserter(newEdges.back().seqSegments));

		return newEdges;
	};

	//Try to collapse each unbranching path.
	//If it is possible - replace with new edges
	auto toCollapse = this->getUnbranchingPaths();
	for (auto& unbranchingPath : toCollapse)
	{
		if (!unbranchingPath.id.strand()) continue;

		GraphPath complPath = _graph.complementPath(unbranchingPath.path);
		auto newEdges = collapseEdges(unbranchingPath.path);
		if (newEdges.size() == unbranchingPath.path.size()) continue;

		std::string addedStr;
		for (auto& edge : newEdges)
		{
			//do not add collapsed short loops
			//if (edge.length() < Parameters::get().minimumOverlap &&
			//	edge.isLooped()) continue;

			GraphEdge addFwd = edge;
			addFwd.edgeId = _graph.newEdgeId();

			GraphEdge addRev(_graph.complementNode(edge.nodeRight),
							 _graph.complementNode(edge.nodeLeft),
							 addFwd.edgeId.rc());

			//complementary segments
			for (auto seqSeg : addFwd.seqSegments)
			{
				addRev.seqSegments.push_back(seqSeg.complement());
			}

			GraphEdge* addedEdge = _graph.addEdge(std::move(addFwd));
			_graph.addEdge(std::move(addRev));

			addedStr += std::to_string(addedEdge->edgeId.signedId()) + " -> ";
		}

		if (addedStr.size() > 4) addedStr.erase(addedStr.size() - 4);
		Logger::get().debug() << "Collapsed: " << unbranchingPath.edgesStr()
			<< " to " << addedStr;

		std::unordered_set<GraphEdge*> toRemove;
		for (auto& edge : unbranchingPath.path) toRemove.insert(edge);
		for (auto& edge : complPath) toRemove.insert(edge);
		for (auto& edge : toRemove) _graph.removeEdge(edge);

		edgesRemoved += unbranchingPath.path.size();
		edgesAdded += newEdges.size();
	}

	Logger::get().debug() << "Removed " << edgesRemoved << " edges";
	Logger::get().debug() << "Added " << edgesAdded << " edges";
}

//converts edges to unbranching paths
std::vector<UnbranchingPath> GraphProcessor::getEdgesPaths() const
{
	std::vector<UnbranchingPath> paths;
	for (auto& edge : _graph.iterEdges())
	{
		GraphPath path = {edge};
		paths.emplace_back(path, edge->edgeId, false,
						   edge->length(), edge->meanCoverage);
		paths.back().repetitive = edge->repetitive;
	}
	return paths;
}

//Finds unbranching paths
std::vector<UnbranchingPath> GraphProcessor::getUnbranchingPaths() const
{
	std::unordered_map<FastaRecord::Id, size_t> edgeIds;
	size_t nextEdgeId = 0;
	auto pathToId = [&edgeIds, &nextEdgeId](GraphPath path)
	{
		if (!edgeIds.count(path.front()->edgeId))
		{
			for (auto edge : path)
			{
				edgeIds[edge->edgeId] = nextEdgeId;
				edgeIds[edge->edgeId.rc()] = nextEdgeId + 1;
			}
			nextEdgeId += 2;
			return FastaRecord::Id(nextEdgeId - 2);
		}
		return FastaRecord::Id(edgeIds[path.front()->edgeId]);
	};

	std::vector<UnbranchingPath> unbranchingPaths;
	std::unordered_set<GraphEdge*> visitedEdges;
	for (auto edge : _graph.iterEdges())
	{
		if (visitedEdges.count(edge)) continue;
		visitedEdges.insert(edge);

		GraphPath traversed;
		traversed.push_back(edge);
		if (!edge->selfComplement)
		{
			GraphNode* curNode = edge->nodeLeft;
			while (!curNode->isBifurcation() &&
				   !curNode->inEdges.empty() &&
				   !visitedEdges.count(curNode->inEdges.front()) &&
				   !curNode->inEdges.front()->selfComplement)
			{
				traversed.push_back(curNode->inEdges.front());
				visitedEdges.insert(traversed.back());
				curNode = curNode->inEdges.front()->nodeLeft;
			}
			std::reverse(traversed.begin(), traversed.end());
			curNode = edge->nodeRight;
			while (!curNode->isBifurcation() &&
				   !curNode->outEdges.empty() &&
				   !visitedEdges.count(curNode->outEdges.front()) &&
				   !curNode->outEdges.front()->selfComplement)
			{
				traversed.push_back(curNode->outEdges.front());
				visitedEdges.insert(traversed.back());
				curNode = curNode->outEdges.front()->nodeRight;
			}
		}

		FastaRecord::Id edgeId = pathToId(traversed);
		bool circular = (traversed.front()->nodeLeft ==
							traversed.back()->nodeRight) &&
						traversed.front()->nodeLeft->outEdges.size() == 1 &&
						traversed.front()->nodeLeft->inEdges.size() == 1;

		bool repetitive = traversed.front()->isRepetitive() ||
						  traversed.back()->isRepetitive();

		int contigLength = 0;
		int64_t sumCov = 0;
		for (auto& edge : traversed)
		{
			contigLength += edge->length();
			sumCov += edge->meanCoverage * edge->length();
		}
		int meanCoverage = contigLength ? sumCov / contigLength : 0;

		unbranchingPaths.emplace_back(traversed, edgeId, circular,
							  contigLength, meanCoverage);
		unbranchingPaths.back().repetitive = repetitive;
	}
	return unbranchingPaths;
}
<|endoftext|>


<commit_before>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbMultiChannelsPolarimetricSynthesisFilter.h"

namespace otb
{
namespace Wrapper
{

class SARPolarSynth : public Application
{
public:
  /** Standard class typedefs. */
  typedef SARPolarSynth                       Self;
  typedef Application                         Superclass;
  typedef itk::SmartPointer<Self>             Pointer;
  typedef itk::SmartPointer<const Self>       ConstPointer;

  typedef MultiChannelsPolarimetricSynthesisFilter<ComplexFloatVectorImageType, FloatImageType>        MCPSFilterType;
  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(SARPolarSynth, otb::Application);

private:
  void DoInit()
  {
    SetName("SARPolarSynth");
    SetDescription("Gives, for each pixel, the power that would have been received by a SAR system with a basis different from the classical (H,V) one (polarimetric synthetis).");

    // Documentation
    SetDocName("SARPolarSynth");
    SetDocLongDescription("This application gives, for each pixel, the power that would have been received by a SAR system with a basis different from the classical (H,V) one (polarimetric synthetis).\n"
						  "The new basis A and B are indicated through two Jones vectors, defined by the user thanks to orientation (psi) and ellipticity (khi) parameters.\n"
						  "These parameters are namely psii, khii, psir and khir. The suffixes (i) and (r) refer to the transmiting antenna and the receiving antenna respectively.\n"
						  "Orientations and ellipticities are given in degrees, and are between -90°/90° and -45°/45° respectively.\n "
						  "\n"
						  "Four polarization architectures can be processed : \n"
						  "1) HH_HV_VH_VV : full polarization, general bistatic case.\n"
						  "2) HH_HV_VV or HH_VH_VV : full polarization, monostatic case (transmitter and receiver are co-located).\n"
						  "3) HH_HV : dual polarization.\n"
						  "4) VH_VV : dual polarization.\n"
						  "The application takes a complex vector image as input, where each band correspond to a particular emission/reception polarization scheme.\n"
						  "User must comply with the band order given above, since the bands are used to build the Sinclair matrix.\n"
						  "\n"
						  "In order to determine the architecture, the application first relies on the number of bands of the input image.\n"
						  "1) Architecture HH_HV_VH_VV is the only one with four bands, there is no possible confusion.\n"
						  "2) Concerning HH_HV_VV and HH_VH_VV architectures, both correspond to a three channels image. But they are processed in the same way, as the Sinclair matrix is symetric in the monostatic case.\n"
						  "3) Finally, the two last architectures (dual polarizations), can't be distinguished only by the number of bands of the input image.\n"
						  "   User must then use the parameters emissionh and emissionv to indicate the architecture of the system : emissionh=1 and emissionv=0 --> HH_HV,  emissionh=0 and emissionv=1 --> VH_VV.\n"
						  "Note : if the architecture is HH_HV, khii and psii are automatically set to 0°/0°; if the architecture is VH_VV, khii and psii are automatically set to 0°/90°.\n"
						  "\n"
						  "It is also possible to force the calculation to co-polar or cross-polar modes.\n"
						  "In the co-polar case, values for psir and khir will be ignored and forced to psii and khii; same as the cross-polar mode, where khir and psir will be forced to psii+90° and -khii.\n"
						  "\n"
						  "Finally, the result of the polarimetric synthetis is expressed in the power domain, through a one-band scalar image.\n"
						  "Note: this application doesn't take into account the terms which do not depend on the polarization of the antennas. \n"
						  "The parameter gain can be used for this purpose.\n"
						  "\n"
						  "The final formula is thus : P = gain x | B^T . [S] . A |², where A ans B are two Jones vectors and S is a Sinclair matrix.");

    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("");

    AddDocTag(Tags::SAR);

    AddParameter(ParameterType_ComplexInputImage,  "in",   "Input Image");
    SetParameterDescription("in", "Input image.");
    AddParameter(ParameterType_OutputImage, "out",  "Output Image");
    SetParameterDescription("out", "Output image.");

    AddParameter(ParameterType_Float,"psii","psii");
    SetParameterDescription("psii","Orientation (transmitting antenna)");
    SetMinimumParameterFloatValue("psii",-90.0);
    SetMaximumParameterFloatValue("psii",90.0);

    AddParameter(ParameterType_Float,"khii","khii");
    SetParameterDescription("khii","Ellipticity (transmitting antenna)");
    SetMinimumParameterFloatValue("khii",-45.0);
    SetMaximumParameterFloatValue("khii",45.0);

    AddParameter(ParameterType_Float,"psir","psir");
    SetParameterDescription("psir","Orientation (receiving antenna)");
    SetMinimumParameterFloatValue("psir",-90.0);
    SetMaximumParameterFloatValue("psir",90.0);

    AddParameter(ParameterType_Float,"khir","khir");
    SetParameterDescription("khir","Ellipticity (receiving antenna)");
    SetMinimumParameterFloatValue("khir",-45.0);
    SetMaximumParameterFloatValue("khir",45.0);

    AddParameter(ParameterType_Int,"emissionh","Emission H");
    SetParameterDescription("emissionh","This parameter is useful in determining the polarization architecture (dual polarization case).");
    SetMinimumParameterIntValue("emissionh",0);
    SetMaximumParameterIntValue("emissionh",1);
    MandatoryOff("emissionh");

    AddParameter(ParameterType_Int,"emissionv","Emission V");
    SetParameterDescription("emissionv","This parameter is useful in determining the polarization architecture (dual polarization case).");
    SetMinimumParameterIntValue("emissionv",0);
    SetMaximumParameterIntValue("emissionv",1);
    MandatoryOff("emissionv");

    AddParameter(ParameterType_Choice, "mode", "Forced mode");
    AddChoice("mode.none","None");
    SetParameterDescription("mode.none","None");
    AddChoice("mode.co","Copolarization");
    SetParameterDescription("mode.none","Copolarization");
    AddChoice("mode.cross","Crosspolarization");
    SetParameterDescription("mode.cross","Crosspolarization");

    AddParameter(ParameterType_Float,"gain","Gain");
    SetParameterDescription("gain","Gain (final power = gain x synthetized power)");
    SetMinimumParameterFloatValue("gain",0.0);
    MandatoryOff("gain");

    AddRAMParameter();

    // Default values
    SetDefaultParameterFloat("psii", 0.);
    SetDefaultParameterFloat("khii", 0.);
    SetDefaultParameterFloat("psir",  0.);
    SetDefaultParameterFloat("khir",  0.);
    SetDefaultParameterInt("emissionh", 0);
    SetDefaultParameterInt("emissionv", 0);
    SetDefaultParameterFloat("gain",  1.0);
	SetDefaultParameterFloat("mode",  0);

    // Doc example parameter settings
    SetDocExampleParameterValue("in", "sar.tif");
    SetDocExampleParameterValue("psii","15.");
    SetDocExampleParameterValue("khii", "5.");
    SetDocExampleParameterValue("psir","-25.");
    SetDocExampleParameterValue("khir", "10.");
    SetDocExampleParameterValue("out", "newbasis.tif");
  }

  void DoUpdateParameters()
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute()
  {

	m_MCPSFilter = MCPSFilterType::New();
	m_MCPSFilter->SetPsiI(GetParameterFloat("psii"));
    m_MCPSFilter->SetKhiI(GetParameterFloat("khii"));
    m_MCPSFilter->SetPsiR(GetParameterFloat("psir"));
    m_MCPSFilter->SetKhiR(GetParameterFloat("khir"));
    m_MCPSFilter->SetEmissionH(GetParameterInt("emissionh"));
    m_MCPSFilter->SetEmissionV(GetParameterInt("emissionv"));
	m_MCPSFilter->SetGain(GetParameterFloat("gain"));
	m_MCPSFilter->SetMode(GetParameterInt("mode"));

    ComplexFloatVectorImageType* inVImage = GetParameterComplexImage("in");
    inVImage->UpdateOutputInformation();
    int nbBands = inVImage->GetNumberOfComponentsPerPixel();
    otbAppLogINFO( << "nbBands = " << nbBands);

	m_MCPSFilter->SetInput(inVImage);

    SetParameterOutputImage("out", m_MCPSFilter->GetOutput());
  }
  //std::vector<itk::ProcessObject::Pointer> m_Ref;
  MCPSFilterType::Pointer m_MCPSFilter;
};

} //end namespace Wrapper
} //end namespace otb

OTB_APPLICATION_EXPORT(otb::Wrapper::SARPolarSynth)
<commit_msg>ENH: removed gain factor<commit_after>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbMultiChannelsPolarimetricSynthesisFilter.h"

namespace otb
{
namespace Wrapper
{

class SARPolarSynth : public Application
{
public:
  /** Standard class typedefs. */
  typedef SARPolarSynth                       Self;
  typedef Application                         Superclass;
  typedef itk::SmartPointer<Self>             Pointer;
  typedef itk::SmartPointer<const Self>       ConstPointer;

  typedef MultiChannelsPolarimetricSynthesisFilter<ComplexFloatVectorImageType, FloatImageType>        MCPSFilterType;
  /** Standard macro */
  itkNewMacro(Self);

  itkTypeMacro(SARPolarSynth, otb::Application);

private:
  void DoInit()
  {
    SetName("SARPolarSynth");
    SetDescription("Gives, for each pixel, the power that would have been received by a SAR system with a basis different from the classical (H,V) one (polarimetric synthetis).");

    // Documentation
    SetDocName("SARPolarSynth");
    SetDocLongDescription("This application gives, for each pixel, the power that would have been received by a SAR system with a basis different from the classical (H,V) one (polarimetric synthetis).\n"
						  "The new basis A and B are indicated through two Jones vectors, defined by the user thanks to orientation (psi) and ellipticity (khi) parameters.\n"
						  "These parameters are namely psii, khii, psir and khir. The suffixes (i) and (r) refer to the transmiting antenna and the receiving antenna respectively.\n"
						  "Orientations and ellipticities are given in degrees, and are between -90°/90° and -45°/45° respectively.\n "
						  "\n"
						  "Four polarization architectures can be processed : \n"
						  "1) HH_HV_VH_VV : full polarization, general bistatic case.\n"
						  "2) HH_HV_VV or HH_VH_VV : full polarization, monostatic case (transmitter and receiver are co-located).\n"
						  "3) HH_HV : dual polarization.\n"
						  "4) VH_VV : dual polarization.\n"
						  "The application takes a complex vector image as input, where each band correspond to a particular emission/reception polarization scheme.\n"
						  "User must comply with the band order given above, since the bands are used to build the Sinclair matrix.\n"
						  "\n"
						  "In order to determine the architecture, the application first relies on the number of bands of the input image.\n"
						  "1) Architecture HH_HV_VH_VV is the only one with four bands, there is no possible confusion.\n"
						  "2) Concerning HH_HV_VV and HH_VH_VV architectures, both correspond to a three channels image. But they are processed in the same way, as the Sinclair matrix is symetric in the monostatic case.\n"
						  "3) Finally, the two last architectures (dual polarizations), can't be distinguished only by the number of bands of the input image.\n"
						  "   User must then use the parameters emissionh and emissionv to indicate the architecture of the system : emissionh=1 and emissionv=0 --> HH_HV,  emissionh=0 and emissionv=1 --> VH_VV.\n"
						  "Note : if the architecture is HH_HV, khii and psii are automatically set to 0°/0°; if the architecture is VH_VV, khii and psii are automatically set to 0°/90°.\n"
						  "\n"
						  "It is also possible to force the calculation to co-polar or cross-polar modes.\n"
						  "In the co-polar case, values for psir and khir will be ignored and forced to psii and khii; same as the cross-polar mode, where khir and psir will be forced to psii+90° and -khii.\n"
						  "\n"
						  "Finally, the result of the polarimetric synthetis is expressed in the power domain, through a one-band scalar image.\n"
						  "Note: this application doesn't take into account the terms which do not depend on the polarization of the antennas. \n"
						  "The parameter gain can be used for this purpose.\n"
						  "\n"
						  "The final formula is thus : P = | B^T . [S] . A |², where A ans B are two Jones vectors and S is a Sinclair matrix.");

    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("");

    AddDocTag(Tags::SAR);

    AddParameter(ParameterType_ComplexInputImage,  "in",   "Input Image");
    SetParameterDescription("in", "Input image.");
    AddParameter(ParameterType_OutputImage, "out",  "Output Image");
    SetParameterDescription("out", "Output image.");

    AddParameter(ParameterType_Float,"psii","psii");
    SetParameterDescription("psii","Orientation (transmitting antenna)");
    SetMinimumParameterFloatValue("psii",-90.0);
    SetMaximumParameterFloatValue("psii",90.0);

    AddParameter(ParameterType_Float,"khii","khii");
    SetParameterDescription("khii","Ellipticity (transmitting antenna)");
    SetMinimumParameterFloatValue("khii",-45.0);
    SetMaximumParameterFloatValue("khii",45.0);

    AddParameter(ParameterType_Float,"psir","psir");
    SetParameterDescription("psir","Orientation (receiving antenna)");
    SetMinimumParameterFloatValue("psir",-90.0);
    SetMaximumParameterFloatValue("psir",90.0);

    AddParameter(ParameterType_Float,"khir","khir");
    SetParameterDescription("khir","Ellipticity (receiving antenna)");
    SetMinimumParameterFloatValue("khir",-45.0);
    SetMaximumParameterFloatValue("khir",45.0);

    AddParameter(ParameterType_Int,"emissionh","Emission H");
    SetParameterDescription("emissionh","This parameter is useful in determining the polarization architecture (dual polarization case).");
    SetMinimumParameterIntValue("emissionh",0);
    SetMaximumParameterIntValue("emissionh",1);
    MandatoryOff("emissionh");

    AddParameter(ParameterType_Int,"emissionv","Emission V");
    SetParameterDescription("emissionv","This parameter is useful in determining the polarization architecture (dual polarization case).");
    SetMinimumParameterIntValue("emissionv",0);
    SetMaximumParameterIntValue("emissionv",1);
    MandatoryOff("emissionv");

    AddParameter(ParameterType_Choice, "mode", "Forced mode");
    AddChoice("mode.none","None");
    SetParameterDescription("mode.none","None");
    AddChoice("mode.co","Copolarization");
    SetParameterDescription("mode.none","Copolarization");
    AddChoice("mode.cross","Crosspolarization");
    SetParameterDescription("mode.cross","Crosspolarization");

    AddRAMParameter();

    // Default values
    SetDefaultParameterFloat("psii", 0.);
    SetDefaultParameterFloat("khii", 0.);
    SetDefaultParameterFloat("psir",  0.);
    SetDefaultParameterFloat("khir",  0.);
    SetDefaultParameterInt("emissionh", 0);
    SetDefaultParameterInt("emissionv", 0);
	SetDefaultParameterFloat("mode",  0);

    // Doc example parameter settings
    SetDocExampleParameterValue("in", "sar.tif");
    SetDocExampleParameterValue("psii","15.");
    SetDocExampleParameterValue("khii", "5.");
    SetDocExampleParameterValue("psir","-25.");
    SetDocExampleParameterValue("khir", "10.");
    SetDocExampleParameterValue("out", "newbasis.tif");
  }

  void DoUpdateParameters()
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute()
  {

	m_MCPSFilter = MCPSFilterType::New();
	m_MCPSFilter->SetPsiI(GetParameterFloat("psii"));
    m_MCPSFilter->SetKhiI(GetParameterFloat("khii"));
    m_MCPSFilter->SetPsiR(GetParameterFloat("psir"));
    m_MCPSFilter->SetKhiR(GetParameterFloat("khir"));
    m_MCPSFilter->SetEmissionH(GetParameterInt("emissionh"));
    m_MCPSFilter->SetEmissionV(GetParameterInt("emissionv"));
	m_MCPSFilter->SetMode(GetParameterInt("mode"));

    ComplexFloatVectorImageType* inVImage = GetParameterComplexImage("in");
    inVImage->UpdateOutputInformation();
    int nbBands = inVImage->GetNumberOfComponentsPerPixel();
    otbAppLogINFO( << "nbBands = " << nbBands);

	m_MCPSFilter->SetInput(inVImage);

    SetParameterOutputImage("out", m_MCPSFilter->GetOutput());
  }
  //std::vector<itk::ProcessObject::Pointer> m_Ref;
  MCPSFilterType::Pointer m_MCPSFilter;
};

} //end namespace Wrapper
} //end namespace otb

OTB_APPLICATION_EXPORT(otb::Wrapper::SARPolarSynth)
<|endoftext|>


<commit_before>#ifndef __RPC_CONNECTIVITY_CONNECTIVITY_HPP__
#define __RPC_CONNECTIVITY_CONNECTIVITY_HPP__

#include <map>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/optional.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/serialization/utility.hpp>
#include "arch/streamed_tcp.hpp"
#include "arch/address.hpp"
#include "utils.hpp"
#include "concurrency/signal.hpp"
#include "concurrency/mutex.hpp"

namespace connectivity {

struct address_t {
    address_t(ip_address_t i, int p) : ip(i), port(p) { }
    address_t() : ip(), port(0) { } // For deserialization
    ip_address_t ip;
    int port;

    bool operator==(const address_t &a) const {
        return ip == a.ip && port == a.port;
    }
    bool operator!=(const address_t &a) const {
        return ip != a.ip || port != a.port;
    }

private:
    friend class ::boost::serialization::access;
    template<class Archive> void serialize(Archive & ar, UNUSED const unsigned int version) {
        ar & ip;
        ar & port;
    }
};

struct cluster_t :
    public home_thread_mixin_t
{
public:

    /* `peer_id_t` is a wrapper around a `boost::uuids::uuid`. Each newly
    created cluster node picks a UUID to be its peer-ID. */
    struct peer_id_t {
        bool operator==(const peer_id_t &p) const {
            return p.uuid == uuid;
        }
        bool operator!=(const peer_id_t &p) const {
            return p.uuid != uuid;
        }
        bool operator<(const peer_id_t &p) const {
            return p.uuid < uuid;
        }

        // TODO: This should not be used. Can we get rid of it without breaking
        // the possibility of using this in an std::map and for deserialization?
        // (other than making all required classes friends and this private)
        peer_id_t() { }
    private:
        friend class cluster_t;
        boost::uuids::uuid uuid;
        peer_id_t(boost::uuids::uuid u) : uuid(u) { }

        friend class boost::serialization::access;
        template<class Archive> void serialize(Archive & ar, UNUSED const unsigned int version) {
            // Not really platform independent...
            boost::serialization::binary_object bin_uuid(&uuid, boost::uuids::uuid::static_size());
            ar & bin_uuid;
        }
    };

    /* Creating a new cluster node, and connecting one cluster to another
    cluster */
    cluster_t(int port);
    ~cluster_t();
    void join(address_t);

    /* `get_me()` returns the `peer_id_t` for this cluster node.
    `get_everybody()` returns all the currently-accessible peers in the
    cluster and their addresses, including us. */
    peer_id_t get_me();
    std::map<peer_id_t, address_t> get_everybody();

    /* `event_watcher_t` is used to watch for any node joining or leaving the
    cluster. `connect_watcher_t` and `disconnect_watcher_t` are used to watch
    for a specific peer connecting or disconnecting. */
    struct event_watcher_t : public intrusive_list_node_t<event_watcher_t> {
        event_watcher_t(cluster_t *);
        virtual ~event_watcher_t();
        virtual void on_connect(peer_id_t) = 0;
        virtual void on_disconnect(peer_id_t) = 0;
    private:
        cluster_t *cluster;
    };
    struct connect_watcher_t : public signal_t, private event_watcher_t {
        connect_watcher_t(cluster_t *, peer_id_t);
    private:
        void on_connect(peer_id_t);
        void on_disconnect(peer_id_t);
        peer_id_t peer;
    };
    struct disconnect_watcher_t : public signal_t, private event_watcher_t {
        disconnect_watcher_t(cluster_t *, peer_id_t);
    private:
        void on_connect(peer_id_t);
        void on_disconnect(peer_id_t);
        peer_id_t peer;
    };

    /* `send_message()` is used to send a message to a specific peer. The
    function will be called with a `std::ostream&` that leads to the peer in
    question. */
    void send_message(peer_id_t, boost::function<void(std::ostream&)>);

    /* `on_message()` is called every time we receive a message. It's called
    with a `std::istream&` that comes from the peer in question. */
    virtual void on_message(peer_id_t, std::istream&) = 0;

private:
    /* We listen for new connections from other peers. (The reason `listener` is
    in a `boost::scoped_ptr` is so that we can stop listening at the beginning
    of our destructor.) */
    boost::scoped_ptr<streamed_tcp_listener_t> listener;
    void on_new_connection(boost::scoped_ptr<streamed_tcp_conn_t> &);

    void join_blocking(address_t address, boost::optional<peer_id_t>, drain_semaphore_t::lock_t);

    void handle(streamed_tcp_conn_t *c,
        boost::optional<peer_id_t> expected_id,
        boost::optional<address_t> expected_address);

    /* `me` is our `peer_id_t`. `routing_table` is all the peers we can
    currently access and their addresses. Peers that are in the process of
    connecting or disconnecting may be in `routing_table` but not in
    `connections`. */
    peer_id_t me;
    std::map<peer_id_t, address_t> routing_table;

    /* `connections` holds open connections to other peers. It has an entry for
    every peer that we are fully and officially connected to, not including us.
    That means it's a subset of the nodes in `routing_table`. */
    struct connection_t {
        streamed_tcp_conn_t *conn;
        mutex_t send_mutex;
    };
    std::map<peer_id_t, connection_t*> connections;

    /* Writes to `everybody` and `connections` are protected by this mutex so
    we never get redundant connections to the same peer. */
    mutex_t new_connection_mutex;

    /* List of everybody watching for connectivity events. `watchers_mutex` is
    so nobody updates `watchers` while we're iterating over it. */
    intrusive_list_t<event_watcher_t> watchers;
    mutex_t watchers_mutex;

    /* Shutdown process stuff */
    cond_t shutdown_cond;
    drain_semaphore_t shutdown_semaphore;
};

} /* namespace connectivity */

#endif /* __RPC_CONNECTIVITY_CONNECTIVITY_HPP__ */
<commit_msg>Fixed a minor error in a comment<commit_after>#ifndef __RPC_CONNECTIVITY_CONNECTIVITY_HPP__
#define __RPC_CONNECTIVITY_CONNECTIVITY_HPP__

#include <map>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/optional.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/serialization/utility.hpp>
#include "arch/streamed_tcp.hpp"
#include "arch/address.hpp"
#include "utils.hpp"
#include "concurrency/signal.hpp"
#include "concurrency/mutex.hpp"

namespace connectivity {

struct address_t {
    address_t(ip_address_t i, int p) : ip(i), port(p) { }
    address_t() : ip(), port(0) { } // For deserialization
    ip_address_t ip;
    int port;

    bool operator==(const address_t &a) const {
        return ip == a.ip && port == a.port;
    }
    bool operator!=(const address_t &a) const {
        return ip != a.ip || port != a.port;
    }

private:
    friend class ::boost::serialization::access;
    template<class Archive> void serialize(Archive & ar, UNUSED const unsigned int version) {
        ar & ip;
        ar & port;
    }
};

struct cluster_t :
    public home_thread_mixin_t
{
public:

    /* `peer_id_t` is a wrapper around a `boost::uuids::uuid`. Each newly
    created cluster node picks a UUID to be its peer-ID. */
    struct peer_id_t {
        bool operator==(const peer_id_t &p) const {
            return p.uuid == uuid;
        }
        bool operator!=(const peer_id_t &p) const {
            return p.uuid != uuid;
        }
        bool operator<(const peer_id_t &p) const {
            return p.uuid < uuid;
        }

        // TODO: This should not be used. Can we get rid of it without breaking
        // the possibility of using this in an std::map and for deserialization?
        // (other than making all required classes friends and this private)
        peer_id_t() { }
    private:
        friend class cluster_t;
        boost::uuids::uuid uuid;
        peer_id_t(boost::uuids::uuid u) : uuid(u) { }

        friend class boost::serialization::access;
        template<class Archive> void serialize(Archive & ar, UNUSED const unsigned int version) {
            // Not really platform independent...
            boost::serialization::binary_object bin_uuid(&uuid, boost::uuids::uuid::static_size());
            ar & bin_uuid;
        }
    };

    /* Creating a new cluster node, and connecting one cluster to another
    cluster */
    cluster_t(int port);
    ~cluster_t();
    void join(address_t);

    /* `get_me()` returns the `peer_id_t` for this cluster node.
    `get_everybody()` returns all the currently-accessible peers in the
    cluster and their addresses, including us. */
    peer_id_t get_me();
    std::map<peer_id_t, address_t> get_everybody();

    /* `event_watcher_t` is used to watch for any node joining or leaving the
    cluster. `connect_watcher_t` and `disconnect_watcher_t` are used to watch
    for a specific peer connecting or disconnecting. */
    struct event_watcher_t : public intrusive_list_node_t<event_watcher_t> {
        event_watcher_t(cluster_t *);
        virtual ~event_watcher_t();
        virtual void on_connect(peer_id_t) = 0;
        virtual void on_disconnect(peer_id_t) = 0;
    private:
        cluster_t *cluster;
    };
    struct connect_watcher_t : public signal_t, private event_watcher_t {
        connect_watcher_t(cluster_t *, peer_id_t);
    private:
        void on_connect(peer_id_t);
        void on_disconnect(peer_id_t);
        peer_id_t peer;
    };
    struct disconnect_watcher_t : public signal_t, private event_watcher_t {
        disconnect_watcher_t(cluster_t *, peer_id_t);
    private:
        void on_connect(peer_id_t);
        void on_disconnect(peer_id_t);
        peer_id_t peer;
    };

    /* `send_message()` is used to send a message to a specific peer. The
    function will be called with a `std::ostream&` that leads to the peer in
    question. */
    void send_message(peer_id_t, boost::function<void(std::ostream&)>);

    /* `on_message()` is called every time we receive a message. It's called
    with a `std::istream&` that comes from the peer in question. */
    virtual void on_message(peer_id_t, std::istream&) = 0;

private:
    /* We listen for new connections from other peers. (The reason `listener` is
    in a `boost::scoped_ptr` is so that we can stop listening at the beginning
    of our destructor.) */
    boost::scoped_ptr<streamed_tcp_listener_t> listener;
    void on_new_connection(boost::scoped_ptr<streamed_tcp_conn_t> &);

    void join_blocking(address_t address, boost::optional<peer_id_t>, drain_semaphore_t::lock_t);

    void handle(streamed_tcp_conn_t *c,
        boost::optional<peer_id_t> expected_id,
        boost::optional<address_t> expected_address);

    /* `me` is our `peer_id_t`. `routing_table` is all the peers we can
    currently access and their addresses. Peers that are in the process of
    connecting or disconnecting may be in `routing_table` but not in
    `connections`. */
    peer_id_t me;
    std::map<peer_id_t, address_t> routing_table;

    /* `connections` holds open connections to other peers. It has an entry for
    every peer that we are fully and officially connected to, not including us.
    That means it's a subset of the nodes in `routing_table`. */
    struct connection_t {
        streamed_tcp_conn_t *conn;
        mutex_t send_mutex;
    };
    std::map<peer_id_t, connection_t*> connections;

    /* Writes to `routing_table` and `connections` are protected by this mutex
    so we never get redundant connections to the same peer. */
    mutex_t new_connection_mutex;

    /* List of everybody watching for connectivity events. `watchers_mutex` is
    so nobody updates `watchers` while we're iterating over it. */
    intrusive_list_t<event_watcher_t> watchers;
    mutex_t watchers_mutex;

    /* These monitors are for use in the shutdown process. When we want to shut
    down, we signal `shutdown_cond` then drain `drain_semaphore`. */
    cond_t shutdown_cond;
    drain_semaphore_t shutdown_semaphore;
};

} /* namespace connectivity */

#endif /* __RPC_CONNECTIVITY_CONNECTIVITY_HPP__ */
<|endoftext|>