text:
/*
 * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
 */

#include "bgp/bgp_peer.h"

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/foreach.hpp>
#include <boost/throw_exception.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/address_v6.hpp>

#include "base/logging.h"
#include "base/task_annotations.h"

#include <sandesh/sandesh_types.h>
#include <sandesh/sandesh.h>

#include "bgp/bgp_config.h"
#include "bgp/bgp_factory.h"
#include "bgp/bgp_log.h"
#include "bgp/bgp_path.h"
#include "bgp/bgp_peer_membership.h"
#include "bgp/bgp_proto.h"
#include "bgp/bgp_server.h"
#include "bgp/bgp_session.h"
#include "bgp/state_machine.h"
#include "bgp/bgp_session_manager.h"
#include "bgp/bgp_peer_types.h"
#include "bgp/bgp_sandesh.h"
#include "bgp/ermvpn/ermvpn_table.h"
#include "bgp/evpn/evpn_table.h"
#include "bgp/inet/inet_table.h"
#include "bgp/l3vpn/inetvpn_table.h"
#include "bgp/inet6/inet6_table.h"
#include "bgp/inet6vpn/inet6vpn_route.h"
#include "bgp/inet6vpn/inet6vpn_table.h"
#include "bgp/routing-instance/peer_manager.h"
#include "bgp/routing-instance/routing_instance.h"
#include "bgp/rtarget/rtarget_table.h"
#include "io/event_manager.h"
#include "net/address.h"
#include "net/bgp_af.h"

using boost::system::error_code;
using namespace std;

class BgpPeer::PeerClose : public IPeerClose {
  public:
    explicit PeerClose(BgpPeer *peer)
        : peer_(peer),
          is_closed_(false),
          manager_(BgpObjectFactory::Create<PeerCloseManager>(peer_)) {
    }

    virtual ~PeerClose() {
    }

    virtual std::string ToString() const {
        return peer_->ToString();
    }

    virtual bool IsCloseGraceful() {

        //
        // If the peer is deleted or administratively held down, do not attempt
        // graceful restart
        //
        if (peer_->IsDeleted() || peer_->IsAdminDown()) return false;

        return peer_->server()->IsPeerCloseGraceful();
    }

    virtual void CustomClose() {
        return peer_->CustomClose();
    }

    // CloseComplete
    //
    // Close process for this peer is complete. Restart the state machine and
    // attempt to bring up session with the neighbor
    //
    virtual bool CloseComplete(bool from_timer, bool gr_cancelled) {
        if (!peer_->IsDeleted()) {

            //
            // If this closure is off graceful restart timer, nothing else to
            // do as we retain the peer based on the configuration
            //
            if (from_timer) return false;

            //
            // Reset peer's state machine
            //
            if (!peer_->IsAdminDown()) peer_->state_machine_->Initialize();

            return false;
        }

        //
        // This peer is deleted. Timer should have already been cancelled
        //
        assert(!from_timer);

        peer_->deleter()->RetryDelete();
        is_closed_ = true;
        return true;
    }

    bool IsClosed() const {
        return is_closed_;
    }

    virtual PeerCloseManager *close_manager() {
        return manager_.get();
    }

    void Close() {
        if (!is_closed_) {
            manager_->Close();
        }
    }

private:
    BgpPeer *peer_;
    bool is_closed_;
    boost::scoped_ptr<PeerCloseManager> manager_;
};

class BgpPeer::PeerStats : public IPeerDebugStats {
public:
    explicit PeerStats(BgpPeer *peer)
        : peer_(peer) {
    }

    // Used when peer flaps.
    // Reset all counters.
    // Socket counters are implicitly cleared because we use a new socket.
    virtual void Clear() {
        error_stats_ = ErrorStats();
        proto_stats_[0] = ProtoStats();
        proto_stats_[1] = ProtoStats();
        update_stats_[0] = UpdateStats();
        update_stats_[1] = UpdateStats();
    }

    // Printable name
    virtual std::string ToString() const {
        return peer_->ToString();
    }
    // Previous State of the peer
    virtual std::string last_state() const {
        return peer_->state_machine()->LastStateName();
    }
    virtual std::string last_state_change_at() const {
        return peer_->state_machine()->last_state_change_at();
    }
    // Last error on this peer
    virtual std::string last_error() const {
        return peer_->state_machine()->last_notification_in_error();
    }
    // Last Event on this peer
    virtual std::string last_event() const {
        return peer_->state_machine()->last_event();
    }

    // When was the Last
    virtual std::string last_flap() const {
        return peer_->last_flap_at();
    }

    // Total number of flaps
    virtual uint32_t num_flaps() const {
        return peer_->flap_count();
    }

    virtual void GetRxProtoStats(ProtoStats *stats) const {
        *stats = proto_stats_[0];
    }

    virtual void GetTxProtoStats(ProtoStats *stats) const {
        *stats = proto_stats_[1];
    }

    virtual void GetRxRouteUpdateStats(UpdateStats *stats)  const {
        *stats = update_stats_[0];
    }

    virtual void GetTxRouteUpdateStats(UpdateStats *stats)  const {
        *stats = update_stats_[1];
    }

    virtual void GetRxSocketStats(IPeerDebugStats::SocketStats *stats) const {
        if (peer_->session()) {
            io::SocketStats socket_stats(peer_->session()->GetSocketStats());
            stats->calls = socket_stats.read_calls;
            stats->bytes = socket_stats.read_bytes;
        }
    }

    virtual void GetTxSocketStats(IPeerDebugStats::SocketStats *stats) const {
        if (peer_->session()) {
            io::SocketStats socket_stats(peer_->session()->GetSocketStats());
            stats->calls = socket_stats.write_calls;
            stats->bytes = socket_stats.write_bytes;
            stats->blocked_count = socket_stats.write_blocked;
            stats->blocked_duration_usecs =
                socket_stats.write_blocked_duration_usecs;
        }
    }

    virtual void UpdateTxUnreachRoute(uint32_t count) {
        update_stats_[1].unreach += count;
    }

    virtual void UpdateTxReachRoute(uint32_t count) {
        update_stats_[1].reach += count;
    }

    // Do nothing for bgp peers.
    virtual void GetRxErrorStats(RxErrorStats *stats) const {
    }

private:
    friend class BgpPeer;

    BgpPeer *peer_;
    ErrorStats error_stats_;
    ProtoStats proto_stats_[2];
    UpdateStats update_stats_[2];
};

class BgpPeer::DeleteActor : public LifetimeActor {
public:
    DeleteActor(BgpPeer *peer)
        : LifetimeActor(peer->server_->lifetime_manager()),
          peer_(peer) {
    }

    virtual bool MayDelete() const {
        CHECK_CONCURRENCY("bgp::Config");
        if (!peer_->peer_close_->IsClosed())
            return false;
        if (!peer_->state_machine_->IsQueueEmpty())
            return false;
        return true;
    }

    virtual void Shutdown() {
        CHECK_CONCURRENCY("bgp::Config");
        peer_->Clear(BgpProto::Notification::PeerDeconfigured);
    }

    virtual void Destroy() {
        CHECK_CONCURRENCY("bgp::Config");
        peer_->PostCloseRelease();
        peer_->rtinstance_->peer_manager()->DestroyIPeer(peer_);
    }

private:
    BgpPeer *peer_;
};

void BgpPeer::SendEndOfRIB(Address::Family family) {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);

    // Bail if there's no session for the peer anymore.
    if (!session_)
        return;

    BgpProto::Update update;
    uint16_t afi;
    uint8_t safi;
    BgpAf::FamilyToAfiSafi(family, afi, safi);
    BgpMpNlri *nlri = new BgpMpNlri(BgpAttribute::MPUnreachNlri, afi, safi);
    update.path_attributes.push_back(nlri);
    uint8_t data[256];
    int result = BgpProto::Encode(&update, data, sizeof(data));
    assert(result > BgpProto::kMinMessageSize);
    session_->Send(data, result, NULL);
}

void BgpPeer::MembershipRequestCallback(IPeer *ipeer, BgpTable *table,
                                        bool established) {
    assert(membership_req_pending_);
    membership_req_pending_--;
    if (!membership_req_pending_ && defer_close_) {
        defer_close_ = false;
        trigger_.Set();
        return;
    }

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_send_state("in sync");
    BGPPeerInfo::Send(peer_info);
    if (established) {
        // Generate End-Of-RIB message
        SendEndOfRIB(table->family());
    }
}

bool BgpPeer::ResumeClose() {
    peer_close_->Close();
    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_send_state("not advertising");
    BGPPeerInfo::Send(peer_info);
    return true;
}

BgpPeer::BgpPeer(BgpServer *server, RoutingInstance *instance,
                 const BgpNeighborConfig *config)
        : server_(server),
          rtinstance_(instance),
          peer_key_(config),
          peer_name_(config->name()),
          config_(config),
          index_(server->RegisterPeer(this)),
          trigger_(boost::bind(&BgpPeer::ResumeClose, this),
                   TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
                   GetIndex()),
          session_(NULL),
          keepalive_timer_(TimerManager::CreateTimer(*server->ioservice(),
                     "BGP keepalive timer")),
          end_of_rib_timer_(TimerManager::CreateTimer(*server->ioservice(),
                   "BGP RTarget EndOfRib timer",
                   TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
                   GetIndex())),
          send_ready_(true),
          admin_down_(false),
          state_machine_(BgpObjectFactory::Create<StateMachine>(this)),
          membership_req_pending_(0),
          defer_close_(false),
          vpn_tables_registered_(false),
          local_as_(config->local_as()),
          peer_as_(config_->peer_as()),
          remote_bgp_id_(0),
          peer_type_((peer_as_ == local_as_) ? BgpProto::IBGP : BgpProto::EBGP),
          policy_(peer_type_, RibExportPolicy::BGP, peer_as_, -1, 0),
          peer_close_(new PeerClose(this)),
          peer_stats_(new PeerStats(this)),
          deleter_(new DeleteActor(this)),
          instance_delete_ref_(this, instance->deleter()),
          flap_count_(0),
          last_flap_(0) {

    if (peer_name_.find(rtinstance_->name()) == 0) {
        peer_basename_ = peer_name_.substr(rtinstance_->name().size() + 1);
    } else {
        peer_basename_ = peer_name_;
    }

    boost::system::error_code ec;
    local_bgp_id_ = config->local_identifier();

    refcount_ = 0;
    configured_families_ = config->address_families();
    sort(configured_families_.begin(), configured_families_.end());
    BOOST_FOREACH(string family, configured_families_) {
        Address::Family fmly = Address::FamilyFromString(family);
        assert(fmly != Address::UNSPEC);
        family_.insert(fmly);
    }
    ProcessAuthKeyChainConfig(config);

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_peer_type(PeerType() == BgpProto::IBGP ?
                            "internal" : "external");
    peer_info.set_local_asn(local_as_);
    peer_info.set_peer_asn(peer_as_);
    peer_info.set_local_id(local_bgp_id_);
    if (!config->address_families().empty())
        peer_info.set_configured_families(config->address_families());

    peer_info.set_peer_address(peer_key_.endpoint.address().to_string());
    BGPPeerInfo::Send(peer_info);
}

BgpPeer::~BgpPeer() {
    assert(GetRefCount() == 0);
    ClearListenSocketAuthKey();
    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_deleted(true);
    BGPPeerInfo::Send(peer_info);
}

void BgpPeer::Initialize() {
    state_machine_->Initialize();
}

void BgpPeer::BindLocalEndpoint(BgpSession *session) {
}

// Just return the first entry for now.
bool BgpPeer::GetBestAuthKey(AuthenticationKey *auth_key) const {
    if (auth_key_chain_.Empty()) {
        return false;
    }
    assert(auth_key);
    AuthKeyChain::const_iterator iter = auth_key_chain_.begin();
    *auth_key = *iter;
    return true;
}

void BgpPeer::ProcessAuthKeyChainConfig(const BgpNeighborConfig *config) {
    const AuthenticationKeyChain &input_key_chain = config->keychain();

    //std::sort(input_key_chain.begin(), input_key_chain.end(),
    //         AuthSortByStartTime());
    if (auth_key_chain_.IsEqual(input_key_chain)) {
        return;
    }

    auth_key_chain_.Update(input_key_chain);
    InstallAuthKeys(session_);
}

void BgpPeer::InstallAuthKeys(TcpSession *session) {
    if (!PeerAddress()) {
        return;
    }
    AuthenticationKey auth_key;
    bool valid = GetBestAuthKey(&auth_key);
    if (valid) {
        if (AuthKeyIsMd5(auth_key)) {
            LogInstallAuthKeys("add", auth_key);
            inuse_auth_key_ = auth_key;
            if (session) {
                session->SetMd5SocketOption(PeerAddress(), auth_key.value);
            }
            SetListenSocketAuthKey();
        }
    } else {
        // If there are no valid available keys but an older one is currently
        // installed, un-install it.
        if (inuse_auth_key_.IsMd5()) {
            LogInstallAuthKeys("delete", inuse_auth_key_);
            ClearListenSocketAuthKey();
            if (session) {
                session->ClearMd5SocketOption(PeerAddress());
            }
            inuse_auth_key_.Reset();
        }
    }
}

void BgpPeer::SetListenSocketAuthKey() {
    if (inuse_auth_key_.IsMd5()) {
        server_->session_manager()->
            SetListenSocketMd5Option(PeerAddress(), inuse_auth_key_.value);
    }
}

void BgpPeer::ClearListenSocketAuthKey() {
    if (inuse_auth_key_.IsMd5()) {
        server_->session_manager()->SetListenSocketMd5Option(PeerAddress(), "");
    }
}

std::string BgpPeer::GetInuseAuthKeyValue() const {
    return inuse_auth_key_.value;
}

bool BgpPeer::AuthKeyIsMd5(const AuthenticationKey &auth_key) {
    return auth_key_chain_.AuthKeyIsMd5(auth_key);
}

void BgpPeer::LogInstallAuthKeys(const std::string &oper,
                                 const AuthenticationKey &auth_key) {
    std::string logstr = "Kernel " + oper + " of key id " + auth_key.id
                          + ", type " + auth_key.KeyTypeToString()
                          + " for peer " + peer_name_;
    BGP_LOG_PEER(Config, this, SandeshLevel::SYS_INFO, BGP_LOG_FLAG_ALL,
                 BGP_PEER_DIR_NA, logstr);
}

void BgpPeer::ConfigUpdate(const BgpNeighborConfig *config) {
    if (IsDeleted())
        return;

    config_ = config;

    // During peer deletion, configuration gets completely deleted. In that
    // case, there is no need to update the rest and flap the peer.
    if (!config_) return;

    AddressFamilyList new_families;
    configured_families_ = config->address_families();
    sort(configured_families_.begin(), configured_families_.end());
    BOOST_FOREACH(string family, configured_families_) {
        Address::Family fmly = Address::FamilyFromString(family);
        assert(fmly != Address::UNSPEC);
        new_families.insert(fmly);
    }

    bool clear_session = false;

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());

    // Check if there is any change in the peer address.
    BgpPeerKey key(config);
    if (peer_key_ != key) {
        // If the peer address is changing, remove the key for the older
        // address. Update with the new peer address and then process the
        // keychain info for the new peer below.
        ClearListenSocketAuthKey();
        peer_key_ = key;
        peer_info.set_peer_address(peer_key_.endpoint.address().to_string());
        clear_session = true;
    }

    ProcessAuthKeyChainConfig(config);

    // Check if there is any change in the configured address families.
    if (family_ != new_families) {
        family_ = new_families;
        clear_session = true;
        peer_info.set_configured_families(config->address_families());
    }

    BgpProto::BgpPeerType old_type = PeerType();
    if (local_as_ != config->local_as()) {
        local_as_ = config->local_as();
        peer_info.set_local_asn(local_as_);
        clear_session = true;
    }

    if (peer_as_ != config->peer_as()) {
        peer_as_ = config->peer_as();
        peer_info.set_peer_asn(peer_as_);
        clear_session = true;
    }

    boost::system::error_code ec;
    uint32_t local_bgp_id = config->local_identifier();
    if (local_bgp_id_ != local_bgp_id) {
        local_bgp_id_ = local_bgp_id;
        peer_info.set_local_id(local_bgp_id_);
        clear_session = true;
    }

    peer_type_ = (peer_as_ == local_as_) ? BgpProto::IBGP : BgpProto::EBGP;

    if (old_type != PeerType()) {
        peer_info.set_peer_type(PeerType() == BgpProto::IBGP ?
                                "internal" : "external");
        policy_.type = peer_type_;
        policy_.as_number = peer_as_;
        clear_session = true;
    }

    if (clear_session) {
        BGP_LOG_PEER(Config, this, SandeshLevel::SYS_INFO, BGP_LOG_FLAG_ALL,
                     BGP_PEER_DIR_NA,
                     "Session cleared due to configuration change");
        BGPPeerInfo::Send(peer_info);
        Clear(BgpProto::Notification::OtherConfigChange);
    }
}

void BgpPeer::ClearConfig() {
    CHECK_CONCURRENCY("bgp::Config");
    config_ = NULL;
}

LifetimeActor *BgpPeer::deleter() {
    return deleter_.get();
}

//
// Check if the given address family has been negotiated with the peer.
//
bool BgpPeer::IsFamilyNegotiated(Address::Family family) {
    // Bail if the family is not configured locally.
    if (!LookupFamily(family))
        return false;

    // Check if the peer advertised it in his Open message.
    switch (family) {
    case Address::INET:
        return MpNlriAllowed(BgpAf::IPv4, BgpAf::Unicast);
        break;
    case Address::INETVPN:
        return MpNlriAllowed(BgpAf::IPv4, BgpAf::Vpn);
        break;
    case Address::RTARGET:
        return MpNlriAllowed(BgpAf::IPv4, BgpAf::RTarget);
        break;
    case Address::EVPN:
        return MpNlriAllowed(BgpAf::L2Vpn, BgpAf::EVpn);
        break;
    case Address::ERMVPN:
        return MpNlriAllowed(BgpAf::IPv4, BgpAf::ErmVpn);
        break;
    case Address::INET6VPN:
        return MpNlriAllowed(BgpAf::IPv6, BgpAf::Vpn);
    default:
        break;
    }

    return false;
}

// Release resources for a peer that is going to be deleted.
void BgpPeer::PostCloseRelease() {
    if (index_ != -1) {
        server_->UnregisterPeer(this);
        index_ = -1;
    }
    TimerManager::DeleteTimer(keepalive_timer_);
    TimerManager::DeleteTimer(end_of_rib_timer_);
}

// IsReady
//
// Check whether this peer is up and ready
//
bool BgpPeer::IsReady() const {
    return state_machine_->get_state() == StateMachine::ESTABLISHED;
}

bool BgpPeer::IsXmppPeer() const {
    return false;
}

uint32_t BgpPeer::bgp_identifier() const {
    return ntohl(remote_bgp_id_);
}

//
// Customized close routing for BgpPeers.
//
// Reset all stored capabilities information and cancel outstanding timers.
//
void BgpPeer::CustomClose() {
    assert(vpn_tables_registered_);
    negotiated_families_.clear();
    ResetCapabilities();
    keepalive_timer_->Cancel();
    end_of_rib_timer_->Cancel();
}

//
// Close this peer by closing all of it's RIBs.
//
// If we haven't registered to VPN tables, do it now before we kick off
// the peer close. This ensures that we will clean up all the VPN routes
// when we do eventually perform the peer close. This handles the corner
// case where the peer has to be closed before VPN tables are registered.
// Need to do this since we add VPN routes before we've registered to the
// VPN tables.
//
// Note that registering to VPN tables will bump membership_req_pending_
// in most normal cases i.e. when at least one VPN family is negotiated.
// Hence we must register to the VPN tables if needed before deciding if
// we need to defer peer close.
//
void BgpPeer::Close() {
    if (!vpn_tables_registered_) {
        RegisterToVpnTables(false);
    }

    if (membership_req_pending_) {
        defer_close_ = true;
        return;
    }

    peer_close_->Close();
    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_send_state("not advertising");
    BGPPeerInfo::Send(peer_info);
}

IPeerClose *BgpPeer::peer_close() {
    return peer_close_.get();
}

IPeerDebugStats *BgpPeer::peer_stats() {
    return peer_stats_.get();
}

const IPeerDebugStats *BgpPeer::peer_stats() const {
    return peer_stats_.get();
}

void BgpPeer::Clear(int subcode) {
    state_machine_->Shutdown(subcode);
}

//
// Check whether this peer has been marked for deletion from configuration
//
bool BgpPeer::IsDeleted() const {
    return deleter_->IsDeleted();
}

bool BgpPeer::IsCloseInProgress() const {
    return (defer_close_ || peer_close_->close_manager()->IsCloseInProgress());
}

StateMachine::State BgpPeer::GetState() const {
    return state_machine_->get_state();
}

const string BgpPeer::GetStateName() const {
    return state_machine_->StateName();
}

BgpSession *BgpPeer::CreateSession() {
    TcpSession *session = server_->session_manager()->CreateSession();
    if (session == NULL) {
        return NULL;
    }

    // Set valid keys, if any, in the socket.
    InstallAuthKeys(session);

    BgpSession *bgp_session = static_cast<BgpSession *>(session);
    BindLocalEndpoint(bgp_session);
    bgp_session->SetPeer(this);
    return bgp_session;
}

void BgpPeer::SetAdminState(bool down) {
    if (admin_down_ != down) {
        admin_down_ = down;
        state_machine_->SetAdminState(down);
    }
}

bool BgpPeer::AcceptSession(BgpSession *session) {
    session->SetPeer(this);

    // Set valid keys, if any, in the socket.
    InstallAuthKeys(session);

    return state_machine_->PassiveOpen(session);
}

//
// Register to tables for negotiated address families.
//
// If the route-target family has been negotiated, defer registration to
// VPN tables till we receive an End-Of-RIB marker for the route-target
// NLRI or till the EndOfRibTimer expires.  This ensures that we do not
// start sending VPN routes to the peer till we know what route targets
// the peer is interested in.
//
// Note that received VPN routes are still processed normally even if we
// haven't registered this BgpPeer to the VPN table in question.
//
void BgpPeer::RegisterAllTables() {
    PeerRibMembershipManager *membership_mgr = server_->membership_mgr();
    RoutingInstance *instance = GetRoutingInstance();

    if (IsFamilyNegotiated(Address::INET)) {
        BgpTable *table = instance->GetTable(Address::INET);
        BGP_LOG_PEER_TABLE(this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
                           table, "Register peer with the table");
        if (table) {
            membership_mgr->Register(this, table, policy_, -1,
                boost::bind(&BgpPeer::MembershipRequestCallback, this, _1, _2,
                            true));
            membership_req_pending_++;
        }
    }

    vpn_tables_registered_ = false;
    if (IsFamilyNegotiated(Address::RTARGET)) {
        BgpTable *table = instance->GetTable(Address::RTARGET);
        BGP_LOG_PEER_TABLE(this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
                           table, "Register peer with the table");
        if (table) {
            membership_mgr->Register(this, table, policy_, -1,
                boost::bind(&BgpPeer::MembershipRequestCallback, this, _1, _2,
                            true));
            membership_req_pending_++;
        }
        StartEndOfRibTimer();
    } else {
        RegisterToVpnTables(true);
    }

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_send_state("not advertising");
    BGPPeerInfo::Send(peer_info);
}

void BgpPeer::SendOpen(TcpSession *session) {
    BgpProto::OpenMessage openmsg;
    openmsg.as_num = local_as_;
    openmsg.holdtime = state_machine_->GetConfiguredHoldTime();
    openmsg.identifier = local_bgp_id_;
    static const uint8_t cap_mp[6][4] = {
        { 0, BgpAf::IPv4,  0, BgpAf::Unicast },
        { 0, BgpAf::IPv4,  0, BgpAf::Vpn },
        { 0, BgpAf::L2Vpn, 0, BgpAf::EVpn },
        { 0, BgpAf::IPv4,  0, BgpAf::RTarget },
        { 0, BgpAf::IPv4,  0, BgpAf::ErmVpn },
        { 0, BgpAf::IPv6,  0, BgpAf::Vpn },
    };

    BgpProto::OpenMessage::OptParam *opt_param =
            new BgpProto::OpenMessage::OptParam;
    if (LookupFamily(Address::INET)) {
        BgpProto::OpenMessage::Capability *cap =
                new BgpProto::OpenMessage::Capability(
                        BgpProto::OpenMessage::Capability::MpExtension,
                        cap_mp[0], 4);

        opt_param->capabilities.push_back(cap);
    }
    if (LookupFamily(Address::INETVPN)) {
        BgpProto::OpenMessage::Capability *cap =
                new BgpProto::OpenMessage::Capability(
                        BgpProto::OpenMessage::Capability::MpExtension,
                        cap_mp[1], 4);
        opt_param->capabilities.push_back(cap);
    }
    if (LookupFamily(Address::EVPN)) {
        BgpProto::OpenMessage::Capability *cap =
                new BgpProto::OpenMessage::Capability(
                        BgpProto::OpenMessage::Capability::MpExtension,
                        cap_mp[2], 4);
        opt_param->capabilities.push_back(cap);
    }
    if (LookupFamily(Address::RTARGET)) {
        BgpProto::OpenMessage::Capability *cap =
                new BgpProto::OpenMessage::Capability(
                        BgpProto::OpenMessage::Capability::MpExtension,
                        cap_mp[3], 4);
        opt_param->capabilities.push_back(cap);
    }
    if (LookupFamily(Address::ERMVPN)) {
        BgpProto::OpenMessage::Capability *cap =
                new BgpProto::OpenMessage::Capability(
                        BgpProto::OpenMessage::Capability::MpExtension,
                        cap_mp[4], 4);
        opt_param->capabilities.push_back(cap);
    }

    if (LookupFamily(Address::INET6VPN)) {
        BgpProto::OpenMessage::Capability *cap =
                new BgpProto::OpenMessage::Capability(
                        BgpProto::OpenMessage::Capability::MpExtension,
                        cap_mp[5], 4);
        opt_param->capabilities.push_back(cap);
    }

    // Add restart capability for generating End-Of-Rib
    const uint8_t restart_cap[2] = { 0x0, 0x0 };
    BgpProto::OpenMessage::Capability *cap =
        new BgpProto::OpenMessage::Capability(
                          BgpProto::OpenMessage::Capability::GracefulRestart,
                          restart_cap, 2);
    opt_param->capabilities.push_back(cap);

    if (opt_param->capabilities.size()) {
        openmsg.opt_params.push_back(opt_param);
    } else {
        delete opt_param;
    }
    uint8_t data[256];
    int result = BgpProto::Encode(&openmsg, data, sizeof(data));
    assert(result > BgpProto::kMinMessageSize);
    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_INFO, BGP_LOG_FLAG_ALL,
                 BGP_PEER_DIR_OUT, "Open "  << openmsg.ToString());
    session->Send(data, result, NULL);

    peer_stats_->proto_stats_[1].open++;
}

void BgpPeer::SendKeepalive(bool from_timer) {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);

    // Bail if there's no session for the peer anymore.
    if (!session_)
        return;

    BgpProto::Keepalive msg;
    uint8_t data[BgpProto::kMinMessageSize];
    int result = BgpProto::Encode(&msg, data, sizeof(data));
    assert(result == BgpProto::kMinMessageSize);
    SandeshLevel::type log_level = from_timer ? Sandesh::LoggingUtLevel() :
                                                SandeshLevel::SYS_INFO;
    BGP_LOG_PEER(Message, this, log_level, BGP_LOG_FLAG_SYSLOG,
                 BGP_PEER_DIR_OUT, "Keepalive");
    send_ready_ = session_->Send(data, result, NULL);

    peer_stats_->proto_stats_[1].keepalive++;
}

static bool SkipUpdateSend() {
    static bool init_;
    static bool skip_;

    if (init_) return skip_;

    skip_ = getenv("BGP_SKIP_UPDATE_SEND") != NULL;
    init_ = true;

    return skip_;
}

bool BgpPeer::SendUpdate(const uint8_t *msg, size_t msgsize) {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);

    // Bail if there's no session for the peer anymore.
    if (!session_)
        return true;

    if (!SkipUpdateSend()) {
        if (Sandesh::LoggingLevel() >= Sandesh::LoggingUtLevel()) {
            BGP_LOG_PEER(Message, this, Sandesh::LoggingUtLevel(),
                         BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_OUT, "Update");
        }
        send_ready_ = session_->Send(msg, msgsize, NULL);
        if (send_ready_) {
            StartKeepaliveTimerUnlocked();
        } else {
            StopKeepaliveTimerUnlocked();
        }
    } else {
        send_ready_ = true;
    }
    peer_stats_->proto_stats_[1].update++;
    inc_tx_route_update();
    if (!send_ready_) {
        BGP_LOG_PEER(Event, this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_ALL,
                     BGP_PEER_DIR_NA, "Send blocked");
        BgpPeerInfoData peer_info;
        peer_info.set_name(ToUVEKey());
        peer_info.set_send_state("not in sync");
        BGPPeerInfo::Send(peer_info);
    }
    return send_ready_;
}

void BgpPeer::SendNotification(BgpSession *session,
        int code, int subcode, const std::string &data) {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    session->SendNotification(code, subcode, data);
    state_machine_->set_last_notification_out(code, subcode, data);
    peer_stats_->proto_stats_[1].notification++;
}

void BgpPeer::SetCapabilities(const BgpProto::OpenMessage *msg) {
    remote_bgp_id_ = msg->identifier;
    capabilities_.clear();
    std::vector<BgpProto::OpenMessage::OptParam *>::const_iterator it;
    for (it = msg->opt_params.begin(); it < msg->opt_params.end(); ++it) {
        capabilities_.insert(capabilities_.end(), (*it)->capabilities.begin(),
                             (*it)->capabilities.end());
        (*it)->capabilities.clear();
    }

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_peer_id(remote_bgp_id_);

    std::vector<std::string> families;
    std::vector<BgpProto::OpenMessage::Capability *>::iterator cap_it;
    for (cap_it = capabilities_.begin(); cap_it < capabilities_.end();
         ++cap_it) {
        if ((*cap_it)->code != BgpProto::OpenMessage::Capability::MpExtension)
            continue;
        uint8_t *data = (*cap_it)->capability.data();
        uint16_t afi = get_value(data, 2);
        uint8_t safi = get_value(data + 3, 1);
        Address::Family family = BgpAf::AfiSafiToFamily(afi, safi);
        if (family == Address::UNSPEC) {
            families.push_back(BgpAf::ToString(afi, safi));
        } else {
            families.push_back(Address::FamilyToString(family));
        }
    }
    peer_info.set_families(families);

    negotiated_families_.clear();
    BOOST_FOREACH(Address::Family family, family_) {
        uint16_t afi;
        uint8_t safi;
        BgpAf::FamilyToAfiSafi(family, afi, safi);
        if (!MpNlriAllowed(afi, safi))
            continue;
        negotiated_families_.push_back(Address::FamilyToString(family));
    }
    sort(negotiated_families_.begin(), negotiated_families_.end());
    peer_info.set_negotiated_families(negotiated_families_);

    BGPPeerInfo::Send(peer_info);
}

// Reset capabilities stored inside peer structure.
//
// When open message is processed, we directly take the capabilities off the
// open message and store inside the peer structure.
//
void BgpPeer::ResetCapabilities() {
    STLDeleteValues(&capabilities_);
    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    std::vector<std::string> families = std::vector<std::string>();
    peer_info.set_families(families);
    std::vector<std::string> negotiated_families = std::vector<std::string>();
    peer_info.set_negotiated_families(negotiated_families);
    BGPPeerInfo::Send(peer_info);
}

bool BgpPeer::MpNlriAllowed(uint16_t afi, uint8_t safi) {
    std::vector<BgpProto::OpenMessage::Capability *>::iterator it;
    for (it = capabilities_.begin(); it < capabilities_.end(); ++it) {
        if ((*it)->code != BgpProto::OpenMessage::Capability::MpExtension)
            continue;
        uint8_t *data = (*it)->capability.data();
        uint16_t af_value = get_value(data, 2);
        uint8_t safi_value = get_value(data + 3, 1);
        if (afi == af_value && safi == safi_value) {
            return true;
        }
    }
    return false;
}

void BgpPeer::ProcessUpdate(const BgpProto::Update *msg) {
    BgpAttrPtr attr = server_->attr_db()->Locate(msg->path_attributes);
    // Check as path loop and neighbor-as
    const BgpAttr *path_attr = attr.get();
    uint32_t flags = 0;

    inc_rx_route_update();

    if (path_attr->as_path() != NULL) {
        // Check whether neighbor has appended its AS to the AS_PATH
        if ((PeerType() == BgpProto::EBGP) &&
            (!path_attr->as_path()->path().AsLeftMostMatch(peer_as()))) {
            flags |= BgpPath::NoNeighborAs;
        }

        // Check for AS_PATH loop
        if (path_attr->as_path()->path().AsPathLoop(local_as_)) {
            flags |= BgpPath::AsPathLooped;
        }
    }

    RoutingInstance *instance = GetRoutingInstance();
    if (msg->nlri.size() || msg->withdrawn_routes.size()) {
        InetTable *table =
            static_cast<InetTable *>(instance->GetTable(Address::INET));
        if (!table) {
            BGP_LOG_PEER(Message, this, SandeshLevel::SYS_CRIT, BGP_LOG_FLAG_ALL,
                         BGP_PEER_DIR_IN, "Cannot find inet table");
            return;
        }

        for (vector<BgpProtoPrefix *>::const_iterator it =
             msg->withdrawn_routes.begin(); it != msg->withdrawn_routes.end();
             ++it) {
            Ip4Prefix prefix;
            int result = Ip4Prefix::FromProtoPrefix((**it), &prefix);
            if (result) {
                BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                    BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                    "Withdrawn route parse error for inet route");
                continue;
            }

            DBRequest req;
            req.oper = DBRequest::DB_ENTRY_DELETE;
            req.data.reset(NULL);
            req.key.reset(new InetTable::RequestKey(prefix, this));
            table->Enqueue(&req);
            inc_rx_route_unreach();
        }

        for (vector<BgpProtoPrefix *>::const_iterator it = msg->nlri.begin();
             it != msg->nlri.end(); ++it) {
            Ip4Prefix prefix;
            int result = Ip4Prefix::FromProtoPrefix((**it), &prefix);
            if (result) {
                BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                    BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                    "NLRI parse error for inet route");
                continue;
            }

            DBRequest req;
            req.oper = DBRequest::DB_ENTRY_ADD_CHANGE;
            req.data.reset(new InetTable::RequestData(attr, flags, 0));
            req.key.reset(new InetTable::RequestKey(prefix, this));
            table->Enqueue(&req);
            inc_rx_route_reach();
        }
    }

    for (std::vector<BgpAttribute *>::const_iterator ait =
            msg->path_attributes.begin();
            ait != msg->path_attributes.end(); ++ait) {
        DBRequest::DBOperation oper;
        if ((*ait)->code == BgpAttribute::MPReachNlri) {
            oper = DBRequest::DB_ENTRY_ADD_CHANGE;
            inc_rx_route_reach();
        } else if ((*ait)->code == BgpAttribute::MPUnreachNlri) {
            oper = DBRequest::DB_ENTRY_DELETE;
            inc_rx_route_unreach();
        } else {
            continue;
        }

        BgpMpNlri *nlri = static_cast<BgpMpNlri *>(*ait);
        if (!nlri) continue;

        Address::Family family = BgpAf::AfiSafiToFamily(nlri->afi, nlri->safi);
        if (!IsFamilyNegotiated(family)) {
            BGP_LOG_PEER(Message, this, SandeshLevel::SYS_NOTICE, BGP_LOG_FLAG_ALL,
                         BGP_PEER_DIR_IN,
                         "AFI "<< nlri->afi << " SAFI " << (int) nlri->safi <<
                         " not allowed");
            continue;
        }

        if ((*ait)->code == BgpAttribute::MPReachNlri)
            attr = GetMpNlriNexthop(nlri, attr);

        switch (family) {
        case Address::INET: {
            InetTable *table =
                static_cast<InetTable *>(instance->GetTable(family));
            assert(table);

            vector<BgpProtoPrefix *>::const_iterator it;
            for (it = nlri->nlri.begin(); it < nlri->nlri.end(); ++it) {
                Ip4Prefix prefix;
                int result = Ip4Prefix::FromProtoPrefix((**it), &prefix);
                if (result) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "MP NLRI parse error for inet route");
                    continue;
                }

                DBRequest req;
                req.oper = oper;
                if (oper == DBRequest::DB_ENTRY_ADD_CHANGE)
                    req.data.reset(new InetTable::RequestData(attr, flags, 0));
                req.key.reset(new InetTable::RequestKey(prefix, this));
                table->Enqueue(&req);
            }
            break;
        }

        case Address::INETVPN: {
            InetVpnTable *table =
              static_cast<InetVpnTable *>(instance->GetTable(family));
            assert(table);

            vector<BgpProtoPrefix *>::const_iterator it;
            for (it = nlri->nlri.begin(); it < nlri->nlri.end(); ++it) {
                InetVpnPrefix prefix;
                uint32_t label = 0;
                int result =
                    InetVpnPrefix::FromProtoPrefix((**it), &prefix, &label);
                if (result) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "MP NLRI parse error for inet-vpn route");
                    continue;
                }

                DBRequest req;
                req.oper = oper;
                if (oper == DBRequest::DB_ENTRY_ADD_CHANGE) {
                    req.data.reset(
                        new InetVpnTable::RequestData(attr, flags, label));
                }
                req.key.reset(new InetVpnTable::RequestKey(prefix, this));
                table->Enqueue(&req);
            }
            break;
        }

        case Address::INET6VPN: {
            Inet6VpnTable *table =
                static_cast<Inet6VpnTable *>(instance->GetTable(family));
            assert(table);

            vector<BgpProtoPrefix *>::const_iterator it;
            for (it = nlri->nlri.begin(); it < nlri->nlri.end(); ++it) {
                Inet6VpnPrefix prefix;
                uint32_t label = 0;
                int result =
                    Inet6VpnPrefix::FromProtoPrefix((**it), &prefix, &label);
                if (result) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "MP NLRI parse error for inet6-vpn route");
                    continue;
                }

                DBRequest req;
                req.oper = oper;
                if (oper == DBRequest::DB_ENTRY_ADD_CHANGE) {
                    req.data.reset(
                        new Inet6VpnTable::RequestData(attr, flags, label));
                }
                req.key.reset(new Inet6VpnTable::RequestKey(prefix, this));
                table->Enqueue(&req);
            }
            break;
        }

        case Address::EVPN: {
            EvpnTable *table =
                static_cast<EvpnTable *>(instance->GetTable(family));
            assert(table);

            vector<BgpProtoPrefix *>::const_iterator it;
            for (it = nlri->nlri.begin(); it < nlri->nlri.end(); ++it) {
                EvpnPrefix prefix;
                BgpAttrPtr new_attr;
                uint32_t label = 0;
                int result = EvpnPrefix::FromProtoPrefix(server_, (**it),
                    (oper == DBRequest::DB_ENTRY_ADD_CHANGE) ? attr.get() : NULL,
                    &prefix, &new_attr, &label);
                if (result) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "MP NLRI parse error for e-vpn route type " << (*it)->type);
                    continue;
                }

                DBRequest req;
                req.oper = oper;
                if (oper == DBRequest::DB_ENTRY_ADD_CHANGE) {
                    req.data.reset(
                        new EvpnTable::RequestData(new_attr, flags, label));
                }
                req.key.reset(new EvpnTable::RequestKey(prefix, this));
                table->Enqueue(&req);
            }
            break;
        }

        case Address::ERMVPN: {
            ErmVpnTable *table;
            table = static_cast<ErmVpnTable *>(instance->GetTable(family));
            assert(table);

            vector<BgpProtoPrefix *>::const_iterator it;
            for (it = nlri->nlri.begin(); it < nlri->nlri.end(); ++it) {
                if (!ErmVpnPrefix::IsValidForBgp((*it)->type)) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "ERMVPN: Unsupported route type " << (*it)->type);
                    continue;
                }

                ErmVpnPrefix prefix;
                int result = ErmVpnPrefix::FromProtoPrefix((**it), &prefix);
                if (result) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "MP NLRI parse error for erm-vpn route type " << (*it)->type);
                    continue;
                }

                DBRequest req;
                req.oper = oper;
                if (oper == DBRequest::DB_ENTRY_ADD_CHANGE) {
                    req.data.reset(
                        new ErmVpnTable::RequestData(attr, flags, 0));
                }
                req.key.reset(new ErmVpnTable::RequestKey(prefix, this));
                table->Enqueue(&req);
            }
            break;
        }

        case Address::RTARGET: {
            RTargetTable *table =
                static_cast<RTargetTable *>(instance->GetTable(family));
            assert(table);

            // Check for End-Of-RIB message.
            if (oper == DBRequest::DB_ENTRY_DELETE && nlri->nlri.empty()) {
                end_of_rib_timer_->Cancel();
                RegisterToVpnTables(true);
                return;
            }

            vector<BgpProtoPrefix *>::const_iterator it;
            for (it = nlri->nlri.begin(); it < nlri->nlri.end(); ++it) {
                RTargetPrefix prefix;
                int result = RTargetPrefix::FromProtoPrefix((**it), &prefix);
                if (result) {
                    BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN,
                        BGP_LOG_FLAG_ALL, BGP_PEER_DIR_IN,
                        "MP NLRI parse error for rtarget route");
                    continue;
                }

                DBRequest req;
                req.oper = oper;
                if (oper == DBRequest::DB_ENTRY_ADD_CHANGE) {
                    req.data.reset(
                        new RTargetTable::RequestData(attr, flags, 0));
                }
                req.key.reset(new RTargetTable::RequestKey(prefix, this));
                table->Enqueue(&req);
            }
            break;
        }

        default:
            continue;
        }
    }
}

void BgpPeer::EndOfRibTimerErrorHandler(string error_name,
                                        string error_message) {
    BGP_LOG_PEER(Timer, this, SandeshLevel::SYS_CRIT, BGP_LOG_FLAG_ALL,
                 BGP_PEER_DIR_NA,
                 "Timer error: " << error_name << " " << error_message);
}

void BgpPeer::RegisterToVpnTables(bool established) {
    CHECK_CONCURRENCY("bgp::StateMachine", "bgp::RTFilter");

    if (vpn_tables_registered_)
        return;
    vpn_tables_registered_ = true;

    PeerRibMembershipManager *membership_mgr = server_->membership_mgr();
    RoutingInstance *instance = GetRoutingInstance();

    if (IsFamilyNegotiated(Address::INETVPN)) {
        BgpTable *table = instance->GetTable(Address::INETVPN);
        BGP_LOG_PEER_TABLE(this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
                           table, "Register peer with the table");
        if (table) {
            membership_mgr->Register(this, table, policy_, -1,
                boost::bind(&BgpPeer::MembershipRequestCallback, this, _1, _2,
                            established));
            membership_req_pending_++;
        }
    }

    if (IsFamilyNegotiated(Address::ERMVPN)) {
        BgpTable *table = instance->GetTable(Address::ERMVPN);
        BGP_LOG_PEER_TABLE(this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
                           table, "Register peer with the table");
        if (table) {
            membership_mgr->Register(this, table, policy_, -1,
                boost::bind(&BgpPeer::MembershipRequestCallback, this, _1, _2,
                            established));
            membership_req_pending_++;
        }
    }

    if (IsFamilyNegotiated(Address::INET6VPN)) {
        BgpTable *vtable = instance->GetTable(Address::INET6VPN);
        BGP_LOG_PEER_TABLE(this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
                           vtable, "Register peer with the table");
        if (vtable) {
            membership_mgr->Register(this, vtable, policy_, -1,
                boost::bind(&BgpPeer::MembershipRequestCallback, this, _1, _2,
                            established));
            membership_req_pending_++;
        }
    }

    if (IsFamilyNegotiated(Address::EVPN)) {
        BgpTable *table = instance->GetTable(Address::EVPN);
        BGP_LOG_PEER_TABLE(this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_TRACE,
                           table, "Register peer with the table");
        if (table) {
            membership_mgr->Register(this, table, policy_, -1,
                boost::bind(&BgpPeer::MembershipRequestCallback, this, _1, _2,
                            established));
            membership_req_pending_++;
        }
    }
}

void BgpPeer::KeepaliveTimerErrorHandler(string error_name,
                                         string error_message) {
    BGP_LOG_PEER(Timer, this, SandeshLevel::SYS_CRIT, BGP_LOG_FLAG_ALL,
                 BGP_PEER_DIR_NA,
                 "Timer error: " << error_name << " " << error_message);
}

bool BgpPeer::EndOfRibTimerExpired() {
    RegisterToVpnTables(true);
    return false;
}

bool BgpPeer::KeepaliveTimerExpired() {
    SendKeepalive(true);

    //
    // Start the timer again, by returning true
    //
    return true;
}

void BgpPeer::StartEndOfRibTimer() {
    uint32_t timeout = 30 * 1000;
    char *time_str = getenv("BGP_RTFILTER_EOR_TIMEOUT");
    if (time_str) {
        timeout = strtoul(time_str, NULL, 0);
    }
    end_of_rib_timer_->Start(timeout,
        boost::bind(&BgpPeer::EndOfRibTimerExpired, this),
        boost::bind(&BgpPeer::EndOfRibTimerErrorHandler, this, _1, _2));
}

void BgpPeer::StartKeepaliveTimerUnlocked() {
    int keepalive_time_msecs = state_machine_->keepalive_time_msecs();
    if (keepalive_time_msecs <= 0)
        return;

    keepalive_timer_->Start(keepalive_time_msecs,
        boost::bind(&BgpPeer::KeepaliveTimerExpired, this),
        boost::bind(&BgpPeer::KeepaliveTimerErrorHandler, this, _1, _2));
}

void BgpPeer::StartKeepaliveTimer() {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    if (session_ && send_ready_)
        StartKeepaliveTimerUnlocked();
}

void BgpPeer::StopKeepaliveTimerUnlocked() {
    keepalive_timer_->Cancel();
}

void BgpPeer::StopKeepaliveTimer() {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    StopKeepaliveTimerUnlocked();
}

bool BgpPeer::KeepaliveTimerRunning() {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    return keepalive_timer_->running();
}

void BgpPeer::SetSendReady() {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    BGP_LOG_PEER(Event, this, SandeshLevel::SYS_DEBUG, BGP_LOG_FLAG_ALL,
                 BGP_PEER_DIR_NA, "Send ready");
    send_ready_ = true;
    if (session_ != NULL)
        StartKeepaliveTimerUnlocked();
    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    peer_info.set_send_state("in sync");
    BGPPeerInfo::Send(peer_info);
}

void BgpPeer::set_session(BgpSession *session) {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    assert(session_ == NULL);
    session_ = session;
}

void BgpPeer::clear_session() {
    tbb::spin_mutex::scoped_lock lock(spin_mutex_);
    if (session_) {
        session_->set_observer(NULL);
        session_->Close();
    }
    session_ = NULL;
}

BgpSession *BgpPeer::session() {
    return session_;
}

string BgpPeer::BytesToHexString(const u_int8_t *msg, size_t size) {
    ostringstream out;
    char buf[4];

    for (size_t i = 0; i < size; i ++) {
        if (!(i % 32)) out << "\n";
        if (!(i %  4)) out << " 0x";
        snprintf(buf, sizeof(buf), "%02X", msg[i]);
        out << buf;
    }

    out << "\n";

    return out.str();
}

bool BgpPeer::ReceiveMsg(BgpSession *session, const u_int8_t *msg,
                         size_t size) {
    ParseErrorContext ec;
    BgpProto::BgpMessage *minfo = BgpProto::Decode(msg, size, &ec);

    if (minfo == NULL) {
        BGP_TRACE_PEER_PACKET(this, msg, size, SandeshLevel::SYS_WARN);
        BGP_LOG_PEER(Message, this, SandeshLevel::SYS_WARN, BGP_LOG_FLAG_ALL,
                     BGP_PEER_DIR_IN,
                     "Error while parsing message at " << ec.type_name);
        state_machine_->OnMessageError(session, &ec);
        return false;
    }

    // Tracing periodic keepalive packets is not necessary.
    if (minfo->type != BgpProto::KEEPALIVE)
        BGP_TRACE_PEER_PACKET(this, msg, size, Sandesh::LoggingUtLevel());

    state_machine_->OnMessage(session, minfo);
    return true;
}

string BgpPeer::ToString() const {
    ostringstream out;

    out << peer_key_.endpoint.address();
    if (peer_key_.endpoint.port() != BgpConfigManager::kDefaultPort) {
        out << ":" << static_cast<unsigned short>(peer_key_.endpoint.port());
    }
    return out.str();
}

string BgpPeer::ToUVEKey() const {
    ostringstream out;

    // XXX Skip default instance names from the logs and uves.
    if (true || !rtinstance_->IsDefaultRoutingInstance()) {
        out << rtinstance_->name() << ":";
    }
    // out << peer_key_.endpoint.address();
    out << server_->localname() << ":";
    out << peer_name();
    return out.str();
}

BgpAttrPtr BgpPeer::GetMpNlriNexthop(BgpMpNlri *nlri, BgpAttrPtr attr) {
    bool update_nh = false;
    IpAddress addr;
    Ip4Address::bytes_type bt = { { 0 } };

    if (nlri->afi == BgpAf::IPv4) {
        if (nlri->safi == BgpAf::Unicast || nlri->safi == BgpAf::RTarget) {
            std::copy(nlri->nexthop.begin(), nlri->nexthop.end(),
                      bt.begin());
            update_nh = true;
        } else if (nlri->safi == BgpAf::Vpn) {
            size_t rdsize = RouteDistinguisher::kSize;
            std::copy(nlri->nexthop.begin() + rdsize,
                      nlri->nexthop.end(), bt.begin());
            update_nh = true;
        }
    } else if (nlri->afi == BgpAf::L2Vpn) {
        if (nlri->safi == BgpAf::EVpn) {
            std::copy(nlri->nexthop.begin(), nlri->nexthop.end(),
                      bt.begin());
            update_nh = true;
        }
    } else if (nlri->afi == BgpAf::IPv6) {
        if (nlri->safi == BgpAf::Vpn) {
            Ip6Address::bytes_type v6_bt = { { 0 } };
            size_t rdsize = RouteDistinguisher::kSize;
            std::copy(nlri->nexthop.begin() + rdsize,
                      nlri->nexthop.end(), v6_bt.begin());
            Ip6Address v6_address(v6_bt);
            if (v6_address.is_v4_mapped()) {
                bt = Address::V4FromV4MappedV6(v6_address).to_bytes();
                update_nh = true;
            }
        }
    }

    // Always update the nexthop in BgpAttr with MpReachNlri->nexthop.
    // NOP in cases <afi,safi> doesn't carry nexthop attribute.
    if (update_nh) {
        addr = Ip4Address(bt);
        attr = server_->attr_db()->UpdateNexthopAndLocate(attr.get(), nlri->afi,
                                                          nlri->safi, addr);
    }
    return attr;
}

void BgpPeer::ManagedDelete() {
    BGP_LOG_PEER(Config, this, SandeshLevel::SYS_INFO, BGP_LOG_FLAG_ALL,
                 BGP_PEER_DIR_NA, "Received request for deletion");
    deleter_->Delete();
}

void BgpPeer::RetryDelete() {
    if (!deleter_->IsDeleted())
        return;
    deleter_->RetryDelete();
}

void BgpPeer::SetDataCollectionKey(BgpPeerInfo *peer_info) const {
    if (rtinstance_) {
        peer_info->set_domain(rtinstance_->name());
    } else {
        peer_info->set_domain(BgpConfigManager::kMasterInstance);
    }

    peer_info->set_ip_address(peer_key_.endpoint.address().to_string());
}

static void FillProtoStats(const IPeerDebugStats::ProtoStats &stats,
                           PeerProtoStats &proto_stats) {
    proto_stats.open = stats.open;
    proto_stats.keepalive = stats.keepalive;
    proto_stats.close = stats.close;
    proto_stats.update = stats.update;
    proto_stats.notification = stats.notification;
    proto_stats.total = stats.open + stats.keepalive + stats.close +
        stats.update + stats.notification;
}

static void FillRouteUpdateStats(const IPeerDebugStats::UpdateStats &stats,
                                 PeerUpdateStats &rt_stats) {
    rt_stats.total = stats.total;
    rt_stats.reach = stats.reach;
    rt_stats.unreach = stats.unreach;
}

static void FillSocketStats(const IPeerDebugStats::SocketStats &socket_stats,
                            PeerSocketStats &peer_socket_stats) {
    peer_socket_stats.calls = socket_stats.calls;
    peer_socket_stats.bytes = socket_stats.bytes;
    if (socket_stats.calls) {
        peer_socket_stats.average_bytes = socket_stats.bytes/socket_stats.calls;
    }
    peer_socket_stats.blocked_count = socket_stats.blocked_count;
    ostringstream os;
    os << boost::posix_time::microseconds(socket_stats.blocked_duration_usecs);
    peer_socket_stats.blocked_duration = os.str();
    if (socket_stats.blocked_count) {
        os.str("");
        os << boost::posix_time::microseconds(
            socket_stats.blocked_duration_usecs/socket_stats.blocked_count);
        peer_socket_stats.average_blocked_duration = os.str();
    }
}

void BgpPeer::FillBgpNeighborDebugState(BgpNeighborResp &resp,
                                        const IPeerDebugStats *peer_state) {
    resp.set_last_state(peer_state->last_state());
    resp.set_last_event(peer_state->last_event());
    resp.set_last_error(peer_state->last_error());
    resp.set_last_state_at(peer_state->last_state_change_at());
    resp.set_flap_count(peer_state->num_flaps());
    resp.set_flap_time(peer_state->last_flap());

    IPeerDebugStats::ProtoStats stats;
    PeerProtoStats proto_stats;
    peer_state->GetRxProtoStats(&stats);
    FillProtoStats(stats, proto_stats);
    resp.set_rx_proto_stats(proto_stats);

    peer_state->GetTxProtoStats(&stats);
    FillProtoStats(stats, proto_stats);
    resp.set_tx_proto_stats(proto_stats);

    IPeerDebugStats::UpdateStats update_stats;
    PeerUpdateStats rt_stats;
    peer_state->GetRxRouteUpdateStats(&update_stats);
    FillRouteUpdateStats(update_stats, rt_stats);
    resp.set_rx_update_stats(rt_stats);

    peer_state->GetTxRouteUpdateStats(&update_stats);
    FillRouteUpdateStats(update_stats, rt_stats);
    resp.set_tx_update_stats(rt_stats);

    IPeerDebugStats::SocketStats socket_stats;
    PeerSocketStats peer_socket_stats;

    peer_state->GetRxSocketStats(&socket_stats);
    FillSocketStats(socket_stats, peer_socket_stats);
    resp.set_rx_socket_stats(peer_socket_stats);

    peer_state->GetTxSocketStats(&socket_stats);
    FillSocketStats(socket_stats, peer_socket_stats);
    resp.set_tx_socket_stats(peer_socket_stats);
}

void BgpPeer::FillNeighborInfo(vector<BgpNeighborResp> *nbr_list,
    bool summary) const {
    BgpNeighborResp nbr;
    nbr.set_peer(peer_basename_);
    nbr.set_deleted(IsDeleted());
    nbr.set_peer_address(peer_key_.endpoint.address().to_string());
    nbr.set_peer_id(Ip4Address(remote_bgp_id_).to_string());
    nbr.set_peer_asn(peer_as());
    nbr.set_encoding("BGP");
    nbr.set_peer_type(PeerType() == BgpProto::IBGP ? "internal" : "external");
    nbr.set_state(state_machine_->StateName());
    nbr.set_local_address(server_->ToString());
    nbr.set_local_id(Ip4Address(htonl(local_bgp_id_)).to_string());
    nbr.set_local_asn(local_as());
    nbr.set_negotiated_hold_time(state_machine_->hold_time());

    if (summary) {
        nbr_list->push_back(nbr);
        return;
    }

    nbr.set_configured_address_families(configured_families_);
    nbr.set_negotiated_address_families(negotiated_families_);
    nbr.set_configured_hold_time(state_machine_->GetConfiguredHoldTime());
    FillBgpNeighborDebugState(nbr, peer_stats_.get());
    PeerRibMembershipManager *mgr = server_->membership_mgr();
    mgr->FillPeerMembershipInfo(this, &nbr);
    nbr_list->push_back(nbr);
}

void BgpPeer::inc_rx_open() {
    peer_stats_->proto_stats_[0].open++;
}

void BgpPeer::inc_rx_keepalive() {
    peer_stats_->proto_stats_[0].keepalive++;
}

size_t BgpPeer::get_rx_keepalive() {
    return peer_stats_->proto_stats_[0].keepalive;
}

size_t BgpPeer::get_tr_keepalive() {
    return peer_stats_->proto_stats_[1].keepalive;
}

void BgpPeer::inc_rx_update() {
    peer_stats_->proto_stats_[0].update++;
}

void BgpPeer::inc_rx_notification() {
    peer_stats_->proto_stats_[0].notification++;
}

size_t BgpPeer::get_rx_notification() {
    return peer_stats_->proto_stats_[0].notification;
}

void BgpPeer::inc_rx_route_update() {
    peer_stats_->update_stats_[0].total++;
}

void BgpPeer::inc_tx_route_update() {
    peer_stats_->update_stats_[1].total++;
}

void BgpPeer::inc_rx_route_reach() {
    peer_stats_->update_stats_[0].reach++;
}

void BgpPeer::inc_rx_route_unreach() {
    peer_stats_->update_stats_[0].unreach++;
}

void BgpPeer::inc_connect_error() {
    peer_stats_->error_stats_.connect_error++;
}

void BgpPeer::inc_connect_timer_expired() {
    peer_stats_->error_stats_.connect_timer++;
}

void BgpPeer::inc_hold_timer_expired() {
    peer_stats_->error_stats_.hold_timer++;
}

void BgpPeer::inc_open_error() {
    peer_stats_->error_stats_.open_error++;
}

void BgpPeer::inc_update_error() {
    peer_stats_->error_stats_.update_error++;
}

size_t BgpPeer::get_connect_error() {
    return peer_stats_->error_stats_.connect_error;
}

size_t BgpPeer::get_connect_timer_expired() {
    return peer_stats_->error_stats_.connect_timer;
}

size_t BgpPeer::get_hold_timer_expired() {
    return peer_stats_->error_stats_.hold_timer;
}

size_t BgpPeer::get_open_error() {
    return peer_stats_->error_stats_.open_error;
}

size_t BgpPeer::get_update_error() {
    return peer_stats_->error_stats_.update_error;
}

std::string BgpPeer::last_flap_at() const {
    if (last_flap_) {
        return integerToString(UTCUsecToPTime(last_flap_));
    } else {
        return "";
    }
}

void BgpPeer::increment_flap_count() {
    flap_count_++;
    last_flap_ = UTCTimestampUsec();

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    PeerFlapInfo flap_info;
    flap_info.set_flap_count(flap_count_);
    flap_info.set_flap_time(last_flap_);
    peer_info.set_flap_info(flap_info);
    BGPPeerInfo::Send(peer_info);
}

void BgpPeer::reset_flap_count() {
    flap_count_ = 0;
    last_flap_ = 0;

    BgpPeerInfoData peer_info;
    peer_info.set_name(ToUVEKey());
    PeerFlapInfo flap_info;
    peer_info.set_flap_info(flap_info);
    BGPPeerInfo::Send(peer_info);
}

ext: cc

max_stars_repo_name: srajag/contrail-controller

max_issues_repo_name: srajag/contrail-controller

max_forks_repo_name: srajag/contrail-controller


text:
//==================================================================================================
/*!
  @file

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_CUMMAX_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_CUMMAX_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-reduction
    This function object computes the cumulate maximum of the argument elements

    @par Header <boost/simd/function/cummax.hpp>

    @par Example:

      @snippet cummax.cpp cummax

    @par Possible output:

      @snippet cummax.txt cummax

  **/
  Value cummax(Value const& x);
} }
#endif

#include <boost/simd/function/scalar/cummax.hpp>
#include <boost/simd/function/simd/cummax.hpp>

#endif

ext: hpp

max_stars_repo_name: SylvainCorlay/pythran

max_issues_repo_name: SylvainCorlay/pythran

max_forks_repo_name: SylvainCorlay/pythran


text:
/**************************************************************************************************
 Software License Agreement (BSD License)

 Copyright (c) 2011-2013, LAR toolkit developers - University of Aveiro - http://lars.mec.ua.pt
 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 University of Aveiro nor the names of its contributors may be used to
   endorse or promote products derived from this software without specific prior written permission.

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

/*
 * setConfig.cpp
 *
 *  Created on: 09-09-2011
 *      Author: konradb3
 */

#include <iostream>

#include <boost/lexical_cast.hpp>

#include <lms1xx/lms1xx.h>

void print_usage() {

	std::cout << " Usage : " << std::endl;
	std::cout << " set_config ip resolution rate " << std::endl;
	std::cout << " Exqample : " << std::endl;
	std::cout << " set_config 192.168.1.2 0.25 50 " << std::endl;
}

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

	LMS1xx laser;
	scanCfg sCfg;

	if(argc < 4) {
		print_usage();
		return 0;
	}

	laser.connect(argv[1]);

	if(!laser.isConnected()) {
		std::cout << "Unable to connect to device at address : " << argv[1] << std::endl;
		return 0;
	}

	sCfg.angleResolution = (int)(boost::lexical_cast<double>(std::string(argv[2])) * 10000);
	sCfg.scaningFrequency = boost::lexical_cast<int>(std::string(argv[3])) * 100;

	laser.login();
	laser.setScanCfg(sCfg);
	laser.saveConfig();

	sCfg = laser.getScanCfg();

	std::cout << "Configuration set to : " << std::endl;
	std::cout << "resolution : " << (double)sCfg.angleResolution/10000.0 << std::endl;
	std::cout << "frequency : " << (double)sCfg.scaningFrequency/100.0 << std::endl;

	laser.disconnect();

	return 0;
}

ext: cpp

max_stars_repo_name: davidtvs/calibration-old

max_issues_repo_name: davidtvs/calibration-old

max_forks_repo_name: davidtvs/calibration-old


text:
#include "corpus.h"
#include "alphabet_collection.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include "logging.h"
#include <boost/assert.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

namespace twpipe {

const char* Corpus::UNK = "_UNK_";
const char* Corpus::BAD0 = "_BAD0_";
const char* Corpus::ROOT = "_ROOT_";
const char* Corpus::SPACE = " ";
const unsigned Corpus::BAD_HED = 10000;
const unsigned Corpus::BAD_DEL = 10000;

void Corpus::parse_units_to_vector(const ParseUnits& parse,
                                   std::vector<unsigned>& heads,
                                   std::vector<unsigned>& deprels) {
  heads.clear();
  deprels.clear();
  /// The first unit is the pseudo root.
  for (unsigned i = 0; i < parse.size(); ++i) {
    heads.push_back(parse[i].head);
    deprels.push_back(parse[i].deprel);
  }
}

void Corpus::vector_to_parse_units(const std::vector<unsigned>& heads,
                                   const std::vector<unsigned>& deprels,
                                   ParseUnits& parse,
                                   bool has_pseudo_root) {
  parse.clear();
  BOOST_ASSERT_MSG(heads.size() == deprels.size(),
                   "In corpus.cc: vector_to_parse, #heads should be equal to #deprels");

  ParseUnit parse_unit;
  if (!has_pseudo_root) {
    parse_unit.head = Corpus::BAD_HED;
    parse_unit.deprel = Corpus::BAD_DEL;
    parse.push_back(parse_unit);
  }
  for (unsigned i = 0; i < heads.size(); ++i) {
    parse_unit.head = heads[i];
    parse_unit.deprel = deprels[i];
    parse.push_back(parse_unit);
  }
}

void Corpus::vector_to_input_units(const std::vector<std::string>& words,
                                   const std::vector<std::string>& postags,
                                   InputUnits & units) {
  // The first element is the pseudo root.
  units.clear();

  Alphabet & word_map = AlphabetCollection::get()->word_map;
  Alphabet & char_map = AlphabetCollection::get()->char_map;
  Alphabet & pos_map = AlphabetCollection::get()->pos_map;

  InputUnit unit;
  unit.wid = word_map.get(Corpus::ROOT);
  unit.pid = pos_map.get(Corpus::ROOT);
  unit.aux_wid = unit.wid;
  unit.word = Corpus::ROOT;
  unit.postag = Corpus::ROOT;
  unit.lemma = Corpus::ROOT;
  unit.feature = Corpus::ROOT;
  units.push_back(unit);

  for (unsigned i = 0; i < words.size(); ++i) {
    const std::string & word = words[i];
    const std::string & postag = postags[i];

    unit.wid = (word_map.contains(word) ? word_map.get(word) : word_map.get(Corpus::UNK));
    unit.pid = pos_map.get(postag);
    unit.aux_wid = unit.wid;
    unit.word = word;
    unit.postag = postag;

    unsigned cur = 0;
    unit.cids.clear();
    while (cur < word.size()) {
      unsigned len = utf8_len(word[cur]);
      std::string ch_str = word.substr(cur, len);
      unit.cids.push_back(
        char_map.contains(ch_str) ? char_map.get(ch_str) : char_map.get(Corpus::UNK)
      );
      cur += len;
    }
    units.push_back(unit);
  }
}

void Corpus::parse_units_to_vector(const ParseUnits & units,
                                   std::vector<unsigned>& heads,
                                   std::vector<std::string>& deprels,
                                   bool add_pseudo_root) {
  heads.clear();
  deprels.clear();

  if (add_pseudo_root) {
    heads.push_back(Corpus::BAD_HED);
    deprels.push_back(Corpus::ROOT);
  }
  // The first element in units is pseduo root.
  for (unsigned i = 1; i < units.size(); ++i) {
    heads.push_back(units[i].head);
    deprels.push_back(AlphabetCollection::get()->deprel_map.get(units[i].deprel));
  }
}

Corpus::Corpus() :
  n_train(0),
  n_devel(0) {
}

void Corpus::load_training_data(const std::string& filename) {
  _INFO << "[corpus] reading training data from: " << filename;

  Alphabet & word_map = AlphabetCollection::get()->word_map;
  Alphabet & char_map = AlphabetCollection::get()->char_map;
  Alphabet & pos_map = AlphabetCollection::get()->pos_map;

  word_map.insert(Corpus::BAD0);
  word_map.insert(Corpus::UNK);
  word_map.insert(Corpus::ROOT);

  char_map.insert(Corpus::BAD0);
  char_map.insert(Corpus::UNK);
  char_map.insert(Corpus::ROOT);
  char_map.insert(Corpus::SPACE);

  pos_map.insert(Corpus::ROOT);

  std::ifstream in(filename);
  if (!in) {
    _ERROR << "[corpus] failed to open the training file.";
    exit(1);
  }

  n_train = 0;
  std::string data = "";
  std::string line;
  while (std::getline(in, line)) {
    boost::algorithm::trim(line);
    if (line.size() == 0) {
      // end for an instance.
      parse_data(data, training_data[n_train], true);
      data = "";
      ++n_train;
    } else {
      data += (line + "\n");
    }
  }
  if (data.size() > 0) {
    parse_data(data, training_data[n_train], true);
    ++n_train;
  }

  _INFO << "[corpus] loaded " << n_train << " training sentences.";
}

void Corpus::load_devel_data(const std::string& filename) {
  _INFO << "[corpus] reading development data from: " << filename;

  Alphabet & word_map = AlphabetCollection::get()->word_map;

  BOOST_ASSERT_MSG(word_map.size() > 1,
                   "[corpus] BAD0 and UNK should be inserted before loading devel data.");

  std::ifstream in(filename);
  if (!in) {
    _ERROR << "[corpus] failed to open the devel file.";
    exit(1);
  }

  n_devel = 0;
  std::string data = "";
  std::string line;
  while (std::getline(in, line)) {
    boost::algorithm::trim(line);
    if (line.size() == 0) {
      parse_data(data, devel_data[n_devel], false);
      data = "";
      ++n_devel;
    } else {
      data += (line + "\n");
    }
  }
  if (data.size() > 0) {
    parse_data(data, devel_data[n_devel], false);
    ++n_devel;
  }

  _INFO << "[corpus] loaded " << n_devel << " development sentences.";
}

unsigned utf8_len(unsigned char x) {
  if (0 == (0x80 & x))         { return 1; }
  else if (0xc0 == (0xe0 & x)) { return 2; }
  else if (0xe0 == (0xf0 & x)) { return 3; }
  else if (0xf0 == (0xf8 & x)) { return 4; }
  else if (0xf8 == (0xfc & x)) { return 5; }
  else if (0xfc == (0xfe & x)) { return 6; }
  assert(false);
}

char32_t utf8_to_unicode_first_(const std::string & s) {
  char32_t wc = 0;
  unsigned char c = s[0];
  if ((c & 0x80) == 0) { wc = c; }
  else if ((c & 0xE0) == 0xC0) { wc = (s[0] & 0x1F) << 6; wc |= (s[1] & 0x3F); }
  else if ((c & 0xF0) == 0xE0) { wc = (s[0] & 0xF) << 12; wc |= (s[1] & 0x3F) << 6;  wc |= (s[2] & 0x3F); }
  else if ((c & 0xF8) == 0xF0) { wc = (s[0] & 0x7) << 18; wc |= (s[1] & 0x3F) << 12; wc |= (s[2] & 0x3F) << 6;  wc |= (s[3] & 0x3F); }
  else if ((c & 0xFC) == 0xF8) { wc = (s[0] & 0x3) << 24; wc |= (s[0] & 0x3F) << 18; wc |= (s[0] & 0x3F) << 12; wc |= (s[0] & 0x3F) << 6;  wc |= (s[0] & 0x3F); }
  else if ((c & 0xFE) == 0xFC) { wc = (s[0] & 0x1) << 30; wc |= (s[0] & 0x3F) << 24; wc |= (s[0] & 0x3F) << 18; wc |= (s[0] & 0x3F) << 12; wc |= (s[0] & 0x3F) << 6; wc |= (s[0] & 0x3F); }
  else { assert(false); }
  return wc;
}

// id form lemma cpos pos feat head deprel phead pdeprel
// 0  1    2     3    4   5    6     7     8     9
void Corpus::parse_data(const std::string& data, Instance & inst, bool train) {
  std::stringstream S(data);
  std::string line;

  inst.input_units.clear();
  inst.parse_units.clear();

  InputUnit input_unit;
  ParseUnit parse_unit;

  Alphabet & word_map = AlphabetCollection::get()->word_map;
  Alphabet & char_map = AlphabetCollection::get()->char_map;
  Alphabet & pos_map = AlphabetCollection::get()->pos_map;
  Alphabet & deprel_map = AlphabetCollection::get()->deprel_map;

  // dummy root at first.
  input_unit.wid = word_map.get(ROOT);
  input_unit.pid = pos_map.get(ROOT);
  input_unit.aux_wid = input_unit.wid;
  input_unit.word = ROOT;
  input_unit.postag = ROOT;
  input_unit.lemma = ROOT;
  input_unit.feature = ROOT;
  inst.input_units.push_back(input_unit);

  parse_unit.head = BAD_HED;
  parse_unit.deprel = BAD_DEL;
  inst.parse_units.push_back(parse_unit);

  std::string guessed_raw_sentence = "";
  while (std::getline(S, line)) {
    std::vector<std::string> tokens;
    boost::algorithm::trim(line);
    if (boost::algorithm::starts_with(line, "# text = ")) {
      inst.raw_sentence = line.substr(9);
    } else if (boost::algorithm::starts_with(line, "#")) {
      continue;
    } else {
      boost::algorithm::split(tokens, line, boost::is_any_of("\t"), boost::token_compress_on);
      BOOST_ASSERT_MSG(tokens.size() > 6, "[corpus] illegal conllu format, number of column less than 6.");

      if (tokens[0].find('.') != std::string::npos || tokens[0].find('-') != std::string::npos) {
        continue;
      }

      if (train) {
        const std::string & word = input_unit.word = tokens[1];
        const std::string & postag = input_unit.postag = tokens[3];
        input_unit.lemma = tokens[2];
        input_unit.feature = tokens[5];

        input_unit.wid = word_map.insert(word);
        input_unit.pid = pos_map.insert(postag);
        input_unit.aux_wid = input_unit.wid;

        unsigned cur = 0;
        input_unit.cids.clear();
        while (cur < word.size()) {
          unsigned len = utf8_len(word[cur]);
          input_unit.cids.push_back(char_map.insert(word.substr(cur, len)));
          cur += len;
        }
        inst.input_units.push_back(input_unit);

        parse_unit.head = boost::lexical_cast<unsigned>(tokens[6]);
        parse_unit.deprel = deprel_map.insert(tokens[7]);
        inst.parse_units.push_back(parse_unit);
      } else {
        const std::string & word = input_unit.word = tokens[1];
        const std::string & postag = input_unit.postag = tokens[3];
        input_unit.lemma = tokens[2];
        input_unit.feature = tokens[5];

        input_unit.wid = (word_map.contains(word) ? word_map.get(word) : word_map.get(UNK));
        input_unit.pid = pos_map.get(postag);
        input_unit.aux_wid = input_unit.wid;

        unsigned cur = 0;
        input_unit.cids.clear();
        while (cur < word.size()) {
          unsigned len = utf8_len(word[cur]);
          std::string ch_str = word.substr(cur, len);
          input_unit.cids.push_back(
            char_map.contains(ch_str) ? char_map.get(ch_str) : char_map.get(Corpus::UNK)
          );
          cur += len;
        }
        inst.input_units.push_back(input_unit);

        parse_unit.head = boost::lexical_cast<unsigned>(tokens[6]);
        parse_unit.deprel = deprel_map.insert(tokens[7]);
        inst.parse_units.push_back(parse_unit);
      }
      if (tokens[9] == "SpaceAfter=No" || tokens[9] == "SpaceAfter=\\n") {
        guessed_raw_sentence += tokens[1];
      } else {
        guessed_raw_sentence += (tokens[1] + " ");
      }
    }
  }
  if (inst.raw_sentence == "") {
    inst.raw_sentence = boost::algorithm::trim_copy(guessed_raw_sentence);
  }
}

void Corpus::get_vocabulary_and_word_count() {
  for (auto& payload : training_data) {
    for (auto& item : payload.second.input_units) {
      training_vocab.insert(item.wid);
      ++counter[item.wid];
    }
  }
}

}


text:
//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/expx2c1.hpp>
#include <boost/simd/as.hpp>
#include <stf.hpp>

STF_CASE_TPL( "Check expx2c1 behavior for integral types"
            , (std::uint32_t)(std::uint64_t)
              (std::int32_t)(std::int64_t)
            )
{
  using boost::simd::as;
  using boost::simd::detail::expx2c1;
  using boost::simd::Expx2c1;
  T ref = T(0);
  STF_TYPE_IS(decltype(Expx2c1<T>()), T);
  STF_EQUAL(Expx2c1<T>(), ref);
  STF_EQUAL(expx2c1( as(T{}) ),ref);
}

STF_CASE_TPL( "Check expx2c1 behavior for double"
            , (double)
            )
{
  using boost::simd::as;
  using boost::simd::detail::expx2c1;
  using boost::simd::Expx2c1;
  T ref = T(128);

  STF_TYPE_IS(decltype(Expx2c1<T>()), T);
  STF_IEEE_EQUAL(Expx2c1<T>(), ref);
  STF_IEEE_EQUAL(expx2c1( as(T{}) ), ref);
}

STF_CASE_TPL( "Check expx2c1 behavior for float"
            , (float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::expx2c1;
  using boost::simd::Expx2c1;
  T ref = T(32);
  STF_TYPE_IS(decltype(Expx2c1<T>()), T);
  STF_IEEE_EQUAL(Expx2c1<T>(), ref);
  STF_IEEE_EQUAL(expx2c1( as(T{}) ), ref);
}


text:
//####### Test module for physical constants ##################

//Define Module name
 #define BOOST_TEST_MODULE "phys/constants"

#include <cmath>

//Include Boost unit tests library & library for floating point comparison
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>

#include "phys_constants.h"
#include "math_constants.h"

using namespace picsar::multi_physics::phys;
using namespace picsar::multi_physics::math;

//Tolerance for double precision calculations
const double double_tolerance = 1.0e-14;

//Tolerance for single precision calculations
const float float_tolerance = 1.0e-7;

//Templated tolerance
template <typename T>
T constexpr tolerance()
{
    if(std::is_same<T,float>::value)
        return float_tolerance;
    else
        return double_tolerance;
}

// ------------- Tests --------------

// ***Test physical constants

template<typename RealType>
void test_case_const_phys()
{
    const auto exp_electron_mass =
        static_cast<RealType>(9.1093837015e-31);
    const auto exp_elementary_charge =
        static_cast<RealType>(1.602176634e-19);
    const auto exp_light_speed =
        static_cast<RealType>(299792458.);
    const auto exp_reduced_plank =
        static_cast<RealType>(1.054571817e-34);
    const auto exp_vacuum_permittivity =
        static_cast<RealType>(8.8541878128e-12);
    const auto exp_vacuum_permeability =
        static_cast<RealType>(1.25663706212e-6);
    const auto exp_fine_structure =
        static_cast<RealType>(0.0072973525693);
    const auto exp_eV =
        static_cast<RealType>(1.602176634e-19);
    const auto exp_KeV =
        static_cast<RealType>(1.602176634e-16);
    const auto exp_MeV =
        static_cast<RealType>(1.602176634e-13);
    const auto exp_GeV =
        static_cast<RealType>(1.602176634e-10);
    const auto exp_sqrt_4_pi_fine_structure =
            static_cast<RealType>(sqrt(
                4.0*pi<double>*fine_structure<double>));
    const auto exp_classical_electron_radius =
        static_cast<RealType>(2.81794032620493e-15);
    const auto exp_schwinger_field =
        static_cast<RealType>(1.32328547494817e18);
    const auto exp_tau_e =
        static_cast<RealType>(9.39963715232933e-24);

    BOOST_CHECK_SMALL(
        (electron_mass<RealType>-exp_electron_mass)/exp_electron_mass,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (elementary_charge<RealType>-exp_elementary_charge)/exp_elementary_charge,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (light_speed<RealType>-exp_light_speed)/exp_light_speed,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (reduced_plank<RealType>-exp_reduced_plank)/exp_reduced_plank,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (vacuum_permittivity<RealType>-exp_vacuum_permittivity)/exp_vacuum_permittivity,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (vacuum_permeability<RealType>-exp_vacuum_permeability)/exp_vacuum_permeability,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (fine_structure<RealType>-exp_fine_structure)/exp_fine_structure,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (eV<RealType>-exp_eV)/exp_eV,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (KeV<RealType>-exp_KeV)/exp_KeV,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (MeV<RealType>-exp_MeV)/exp_MeV,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (GeV<RealType>-exp_GeV)/exp_GeV,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
            (sqrt_4_pi_fine_structure<RealType>-exp_sqrt_4_pi_fine_structure)/
            exp_sqrt_4_pi_fine_structure, tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (classical_electron_radius<RealType>-exp_classical_electron_radius)/exp_classical_electron_radius,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (schwinger_field<RealType>-exp_schwinger_field)/exp_schwinger_field,
        tolerance<RealType>());

    BOOST_CHECK_SMALL(
        (tau_e<RealType>-exp_tau_e)/exp_tau_e,
        tolerance<RealType>());
}

BOOST_AUTO_TEST_CASE( picsar_const_phys )
{
    test_case_const_phys<double>();
    test_case_const_phys<float>();
}

// *******************************

max_stars_repo_name: LDAmorim/picsar

max_issues_repo_name: LDAmorim/picsar

max_forks_repo_name: LDAmorim/picsar