/*
http://example.com

A thread-safe logging console with a line editor for windows.

Based on linenoise win32 port,
All rights reserved.
The original linenoise can be found at: http://example.com

*/

#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>

#include <memory>

#include <md5wrapper.h>

using std::cout;
using std::cerr;
using std::endl;

typedef unsigned char patch_byte;

struct BinaryPatch {
    struct Byte {
        unsigned offset;
        patch_byte old_val, new_val;
    };
    enum State {
        Conflict = 0,
        Unapplied = 1,
        Applied = 2,
        Partial = 3
    };

    std::vector<Byte> entries;

    bool loadDIF(std::string name);
    State checkState(const patch_byte *ptr, size_t len);

    void apply(patch_byte *ptr, size_t len, bool newv);
};

inline bool is_hex(char c)
{
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

bool BinaryPatch::loadDIF(std::string name)
{
    entries.clear();

    std::ifstream infile(name);
    if(infile.bad())
    {
        cerr << "Cannot open file: " << name << endl;
        return false;
    }

    std::string s;
    while(std::getline(infile, s))
    {
        // Parse lines that begin with "[0-9a-f]+:"
        size_t idx = s.find(':');
        if (idx == std::string::npos || idx == 0 || idx > 8)
            continue;

        bool ok = true;
        for (size_t i = 0; i < idx; i++)
            if (!is_hex(s[i]))
                ok = false;
        if (!ok)
            continue;

        unsigned off, oval, nval;
        int nchar = 0;
        int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);

        if (cnt < 3)
        {
            cerr << "Could not parse: " << s << endl;
            return false;
        }

        for (size_t i = nchar; i < s.size(); i++)
        {
            if (!isspace(s[i]))
            {
                cerr << "Garbage at end of line: " << s << endl;
                return false;
            }
        }

        if (oval >= 256 || nval >= 256)
        {
            cerr << "Invalid byte values: " << s << endl;
            return false;
        }

        Byte bv = { off, patch_byte(oval), patch_byte(nval) };
        entries.push_back(bv);
    }

    if (entries.empty())
    {
        cerr << "No lines recognized." << endl;
        return false;
    }

    return true;
}

BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
    int state = 0;

    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];

        if (bv.offset >= len)
        {
            cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
            return Conflict;
        }

        patch_byte cv = ptr[bv.offset];
        if (bv.old_val == cv)
            state |= Unapplied;
        else if (bv.new_val == cv)
            state |= Applied;
        else
        {
            cerr << std::hex << bv.offset << ": "
                 << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
                 << ", but currently " << unsigned(cv) << std::dec << endl;
            return Conflict;
        }
    }

    return State(state);
}

void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];
        assert (bv.offset < len);

        ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
    }
}

bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "rb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    fseek(f, 0, SEEK_END);
    pvec->resize(ftell(f));
    fseek(f, 0, SEEK_SET);
    size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
    fclose(f);

    return cnt == pvec->size();
}

bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "wb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
    fclose(f);

std::string compute_hash(const std::vector<patch_byte> &pvec)
{
    md5wrapper md5;
    return md5.getHashFromBytes(pvec.data(), pvec.size());
}

int main (int argc, char *argv[])
{
    if (argc <= 3)
    {
        cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
        return 2;
    }

    std::string cmd = argv[1];

    if (cmd != "check" && cmd != "apply" && cmd != "remove")
    {
        cerr << "Invalid command: " << cmd << endl;
        return 2;
    }

    std::string exe_file = argv[2];
    std::vector<patch_byte> bindata;
    if (!load_file(&bindata, exe_file))
        return 2;

    BinaryPatch patch;
    if (!patch.loadDIF(argv[3]))
        return 2;

    BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
    if (state == BinaryPatch::Conflict)
        return 1;

    if (cmd == "check")
    {
        switch (state)
        {
        case BinaryPatch::Unapplied:
            cout << "Currently not applied." << endl;
            break;
        case BinaryPatch::Applied:
            cout << "Currently applied." << endl;
            break;
        case BinaryPatch::Partial:
            cout << "Currently partially applied." << endl;
            break;
        default:
            break;
        }

        return 0;
    }
    else if (cmd == "apply")
    {
        if (state == BinaryPatch::Applied)
        {
            cout << "Already applied." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), true);
    }
    else if (cmd == "remove")
    {
        if (state == BinaryPatch::Unapplied)
        {
            cout << "Already removed." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), false);
    }

    if (!save_file(bindata, exe_file + ".bak"))
    {
        cerr << "Could not create backup." << endl;
        return 1;
    }

    if (!save_file(bindata, exe_file))
        return 1;

    cout << "Patched " << patch.entries.size()
         << " bytes, new hash: " << compute_hash(bindata) << endl;
    return 0;
}


/*
http://example.com

A thread-safe logging console with a line editor for windows.

Based on linenoise win32 port,
All rights reserved.
The original linenoise can be found at: http://example.com

*/

#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>

#include <memory>

#include <md5wrapper.h>

using std::cout;
using std::cerr;
using std::endl;

typedef unsigned char patch_byte;

struct BinaryPatch {
    struct Byte {
        unsigned offset;
        patch_byte old_val, new_val;
    };
    enum State {
        Conflict = 0,
        Unapplied = 1,
        Applied = 2,
        Partial = 3
    };

    std::vector<Byte> entries;

    bool loadDIF(std::string name);
    State checkState(const patch_byte *ptr, size_t len);

    void apply(patch_byte *ptr, size_t len, bool newv);
};

inline bool is_hex(char c)
{
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

bool BinaryPatch::loadDIF(std::string name)
{
    entries.clear();

    std::ifstream infile(name);
    if(infile.bad())
    {
        cerr << "Cannot open file: " << name << endl;
        return false;
    }

    std::string s;
    while(std::getline(infile, s))
    {
        // Parse lines that begin with "[0-9a-f]+:"
        size_t idx = s.find(':');
        if (idx == std::string::npos || idx == 0 || idx > 8)
            continue;

        bool ok = true;
        for (size_t i = 0; i < idx; i++)
            if (!is_hex(s[i]))
                ok = false;
        if (!ok)
            continue;

        unsigned off, oval, nval;
        int nchar = 0;
        int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);

        if (cnt < 3)
        {
            cerr << "Could not parse: " << s << endl;
            return false;
        }

        for (size_t i = nchar; i < s.size(); i++)
        {
            if (!isspace(s[i]))
            {
                cerr << "Garbage at end of line: " << s << endl;
                return false;
            }
        }

        if (oval >= 256 || nval >= 256)
        {
            cerr << "Invalid byte values: " << s << endl;
            return false;
        }

        Byte bv = { off, patch_byte(oval), patch_byte(nval) };
        entries.push_back(bv);
    }

    if (entries.empty())
    {
        cerr << "No lines recognized." << endl;
        return false;
    }

    return true;
}

BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
    int state = 0;

    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];

        if (bv.offset >= len)
        {
            cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
            return Conflict;
        }

        patch_byte cv = ptr[bv.offset];
        if (bv.old_val == cv)
            state |= Unapplied;
        else if (bv.new_val == cv)
            state |= Applied;
        else
        {
            cerr << std::hex << bv.offset << ": "
                 << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
                 << ", but currently " << unsigned(cv) << std::dec << endl;
            return Conflict;
        }
    }

    return State(state);
}

void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];
        assert (bv.offset < len);

        ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
    }
}

bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "rb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    fseek(f, 0, SEEK_END);
    pvec->resize(ftell(f));
    fseek(f, 0, SEEK_SET);
    size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
    fclose(f);

    return cnt == pvec->size();
}

bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "wb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
    fclose(f);

    return cnt == pvec.size();
}

std::string compute_hash(const std::vector<patch_byte> &pvec)
{
    md5wrapper md5;
    return md5.getHashFromBytes(pvec.data(), pvec.s

}

int main (int argc, char *argv[])
{
    if (argc <= 3)
    {
        cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
        return 2;
    }

    std::string cmd = argv[1];

    if (cmd != "check" && cmd != "apply" && cmd != "remove")
    {
        cerr << "Invalid command: " << cmd << endl;
        return 2;
    }

    std::string exe_file = argv[2];
    std::vector<patch_byte> bindata;
    if (!load_file(&bindata, exe_file))
        return 2;

    BinaryPatch patch;
    if (!patch.loadDIF(argv[3]))
        return 2;

    BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
    if (state == BinaryPatch::Conflict)
        return 1;

    if (cmd == "check")
    {
        switch (state)
        {
        case BinaryPatch::Unapplied:
            cout << "Currently not applied." << endl;
            break;
        case BinaryPatch::Applied:
            cout << "Currently applied." << endl;
            break;
        case BinaryPatch::Partial:
            cout << "Currently partially applied." << endl;
            break;
        default:
            break;
        }

        return 0;
    }
    else if (cmd == "apply")
    {
        if (state == BinaryPatch::Applied)
        {
            cout << "Already applied." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), true);
    }
    else if (cmd == "remove")
    {
        if (state == BinaryPatch::Unapplied)
        {
            cout << "Already removed." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), false);
    }

    if (!save_file(bindata, exe_file + ".bak"))
    {
        cerr << "Could not create backup." << endl;
        return 1;
    }

    if (!save_file(bindata, exe_file))
        return 1;

    cout << "Patched " << patch.entries.size()
         << " bytes, new hash: " << compute_hash(bindata) << endl;
    return 0;
}


if (cmd != "check" && cmd != "apply" && cmd != "remove")
    {
        cerr << "Invalid command: " << cmd << endl;
        return 2;
    }

/*
http://example.com

A thread-safe logging console with a line editor for windows.

Based on linenoise win32 port,
All rights reserved.
The original linenoise can be found at: http://example.com

*/

#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>

#include <memory>

#include <md5wrapper.h>

using std::cout;
using std::cerr;
using std::endl;

typedef unsigned char patch_byte;

struct BinaryPatch {
    struct Byte {
        unsigned offset;
        patch_byte old_val, new_val;
    };
    enum State {
        Conflict = 0,
        Unapplied = 1,
        Applied = 2,
        Partial = 3
    };

    std::vector<Byte> entries;

    bool loadDIF(std::string name);
    State checkState(const patch_byte *ptr, size_t len);

    void apply(patch_byte *ptr, size_t len, bool newv);
};

inline bool is_hex(char c)
{
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

bool BinaryPatch::loadDIF(std::string name)
{
    entries.clear();

    std::ifstream infile(name);
    if(infile.bad())
    {
        cerr << "Cannot open file: " << name << endl;
        return false;
    }

    std::string s;
    while(std::getline(infile, s))
    {
        // Parse lines that begin with "[0-9a-f]+:"
        size_t idx = s.find(':');
        if (idx == std::string::npos || idx == 0 || idx > 8)
            continue;

        bool ok = true;
        for (size_t i = 0; i < idx; i++)
            if (!is_hex(s[i]))
                ok = false;
        if (!ok)
            continue;

        unsigned off, oval, nval;
        int nchar = 0;
        int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);

        if (cnt < 3)
        {
            cerr << "Could not parse: " << s << endl;
            return false;
        }

        for (size_t i = nchar; i < s.size(); i++)
        {
            if (!isspace(s[i]))
            {
                cerr << "Garbage at end of line: " << s << endl;
                return false;
            }
        }

        if (oval >= 256 || nval >= 256)
        {
            cerr << "Invalid byte values: " << s << endl;
            return false;
        }

        Byte bv = { off, patch_byte(oval), patch_byte(nval) };
        entries.push_back(bv);
    }

    if (entries.empty())
    {
        cerr << "No lines recognized." << endl;
        return false;
    }

    return true;
}

BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
    int state = 0;

    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];

        if (bv.offset >= len)
        {
            cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
            return Conflict;
        }

        patch_byte cv = ptr[bv.offset];
        if (bv.old_val == cv)
            state |= Unapplied;
        else if (bv.new_val == cv)
            state |= Applied;
        else
        {
            cerr << std::hex << bv.offset << ": "
                 << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
                 << ", but currently " << unsigned(cv) << std::dec << endl;
            return Conflict;
        }
    }

    return State(state);
}

void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];
        assert (bv.offset < len);

        ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
    }
}

bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "rb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    fseek(f, 0, SEEK_END);
    pvec->resize(ftell(f));
    fseek(f, 0, SEEK_SET);
    size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
    fclose(f);

    return cnt == pvec->size();
}

bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "wb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
    fclose(f);

    return cnt == pvec.size();
}

std::string compute_hash(const std::vector<patch_byte> &pvec)
{
    md5wrapper md5;
    return md5.getHashFromBytes(pvec.data(), pvec.size());
}

int main (int argc, char *argv[])
{
    if (argc <= 3)
    {
        cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
        return 2;
    }

    std::string cmd = argv[1];

    std::string exe_file = argv[2];
    std::vector<patch_byte> bindata;
    if (!load_file(&bindata, exe_file))
        return 2;

    BinaryPatch patch;
    if (!patch.loadDIF(argv[3]))
        return 2;

    BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
    if (state == BinaryPatch::Conflict)
        return 1;

    if (cmd == "check")
    {
        switch (state)
        {
        case BinaryPatch::Unapplied:
            cout << "Currently not applied." << endl;
            break;
        case BinaryPatch::Applied:
            cout << "Currently applied." << endl;
            break;
        case BinaryPatch::Partial:
            cout << "Currently partially applied." << endl;
            break;
        default:
            break;
        }

        return 0;
    }
    else if (cmd == "apply")
    {
        if (state == BinaryPatch::Applied)
        {
            cout << "Already applied." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), true);
    }
    else if (cmd == "remove")
    {
        if (state == BinaryPatch::Unapplied)
        {
            cout << "Already removed." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), false);
    }

    if (!save_file(bindata, exe_file + ".bak"))
    {
        cerr << "Could not create backup." << endl;
        return 1;
    }

    if (!save_file(bindata, exe_file))
        return 1;

    cout << "Patched " << patch.entries.size()
         << " bytes, new hash: " << compute_hash(bindata) << endl;
    return 0;
}


switch (state)
        {
        case BinaryPatch::Unapplied:
            cout << "Currently not applied." << endl;
            break;
        case BinaryPatch::Applied:
            cout << "Currently applied." << endl;
            break;
        case BinaryPatch::Partial:
            cout << "Currently partially applied." << endl;
            break;
        default:
            break;
        }

/*
http://example.com

A thread-safe logging console with a line editor for windows.

Based on linenoise win32 port,
All rights reserved.
The original linenoise can be found at: http://example.com

*/

#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>

#include <memory>

#include <md5wrapper.h>

using std::cout;
using std::cerr;
using std::endl;

typedef unsigned char patch_byte;

struct BinaryPatch {
    struct Byte {
        unsigned offset;
        patch_byte old_val, new_val;
    };
    enum State {
        Conflict = 0,
        Unapplied = 1,
        Applied = 2,
        Partial = 3
    };

    std::vector<Byte> entries;

    bool loadDIF(std::string name);
    State checkState(const patch_byte *ptr, size_t len);

    void apply(patch_byte *ptr, size_t len, bool newv);
};

inline bool is_hex(char c)
{
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

bool BinaryPatch::loadDIF(std::string name)
{
    entries.clear();

    std::ifstream infile(name);
    if(infile.bad())
    {
        cerr << "Cannot open file: " << name << endl;
        return false;
    }

    std::string s;
    while(std::getline(infile, s))
    {
        // Parse lines that begin with "[0-9a-f]+:"
        size_t idx = s.find(':');
        if (idx == std::string::npos || idx == 0 || idx > 8)
            continue;

        bool ok = true;
        for (size_t i = 0; i < idx; i++)
            if (!is_hex(s[i]))
                ok = false;
        if (!ok)
            continue;

        unsigned off, oval, nval;
        int nchar = 0;
        int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);

        if (cnt < 3)
        {
            cerr << "Could not parse: " << s << endl;
            return false;
        }

        for (size_t i = nchar; i < s.size(); i++)
        {
            if (!isspace(s[i]))
            {
                cerr << "Garbage at end of line: " << s << endl;
                return false;
            }
        }

        if (oval >= 256 || nval >= 256)
        {
            cerr << "Invalid byte values: " << s << endl;
            return false;
        }

        Byte bv = { off, patch_byte(oval), patch_byte(nval) };
        entries.push_back(bv);
    }

    if (entries.empty())
    {
        cerr << "No lines recognized." << endl;
        return false;
    }

    return true;
}

BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
    int state = 0;

    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];

        if (bv.offset >= len)
        {
            cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
            return Conflict;
        }

        patch_byte cv = ptr[bv.offset];
        if (bv.old_val == cv)
            state |= Unapplied;
        else if (bv.new_val == cv)
            state |= Applied;
        else
        {
            cerr << std::hex << bv.offset << ": "
                 << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
                 << ", but currently " << unsigned(cv) << std::dec << endl;
            return Conflict;
        }
    }

    return State(state);
}

void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];
        assert (bv.offset < len);

        ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
    }
}

bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "rb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    fseek(f, 0, SEEK_END);
    pvec->resize(ftell(f));
    fseek(f, 0, SEEK_SET);
    size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
    fclose(f);

    return cnt == pvec->size();
}

bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "wb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
    fclose(f);

    return cnt == pvec.size();
}

std::string compute_hash(const std::vector<patch_byte> &pvec)
{
    md5wrapper md5;
    return md5.getHashFromBytes(pvec.data(), pvec.size());
}

int main (int argc, char *argv[])
{
    if (argc <= 3)
    {
        cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
        return 2;
    }

    std::string cmd = argv[1];

    if (cmd != "check" && cmd != "apply" && cmd != "remove")
    {
        cerr << "Invalid command: " << cmd << endl;
        return 2;
    }

    std::string exe_file = argv[2];
    std::vector<patch_byte> bindata;
    if (!load_file(&bindata, exe_file))
        return 2;

    BinaryPatch patch;
    if (!patch.loadDIF(argv[3]))
        return 2;

    BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
    if (state == BinaryPatch::Conflict)
        return 1;

    if (cmd == "check")
    {

        return 0;
    }
    else if (cmd == "apply")
    {
        if (state == BinaryPatch::Applied)
        {
            cout << "Already applied." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), true);
    }
    else if (cmd == "remove")
    {
        if (state == BinaryPatch::Unapplied)
        {
            cout << "Already removed." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), false);
    }

    if (!save_file(bindata, exe_file + ".bak"))
    {
        cerr << "Could not create backup." << endl;
        return 1;
    }

    if (!save_file(bindata, exe_file))
        return 1;

    cout << "Patched " << patch.entries.size()
         << " bytes, new hash: " << compute_hash(bindata) << endl;
    return 0;
}


for (size_t i = nchar; i < s.size(); i++)
        {
            if (!isspace(s[i]))
            {
                cerr << "Garbage at end of line: " << s << endl;
                return false;
            }
        }

/*
http://example.com

A thread-safe logging console with a line editor for windows.

Based on linenoise win32 port,
All rights reserved.
The original linenoise can be found at: http://example.com

*/

#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>

#include <memory>

#include <md5wrapper.h>

using std::cout;
using std::cerr;
using std::endl;

typedef unsigned char patch_byte;

struct BinaryPatch {
    struct Byte {
        unsigned offset;
        patch_byte old_val, new_val;
    };
    enum State {
        Conflict = 0,
        Unapplied = 1,
        Applied = 2,
        Partial = 3
    };

    std::vector<Byte> entries;

    bool loadDIF(std::string name);
    State checkState(const patch_byte *ptr, size_t len);

    void apply(patch_byte *ptr, size_t len, bool newv);
};

inline bool is_hex(char c)
{
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

bool BinaryPatch::loadDIF(std::string name)
{
    entries.clear();

    std::ifstream infile(name);
    if(infile.bad())
    {
        cerr << "Cannot open file: " << name << endl;
        return false;
    }

    std::string s;
    while(std::getline(infile, s))
    {
        // Parse lines that begin with "[0-9a-f]+:"
        size_t idx = s.find(':');
        if (idx == std::string::npos || idx == 0 || idx > 8)
            continue;

        bool ok = true;
        for (size_t i = 0; i < idx; i++)
            if (!is_hex(s[i]))
                ok = false;
        if (!ok)
            continue;

        unsigned off, oval, nval;
        int nchar = 0;
        int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);

        if (cnt < 3)
        {
            cerr << "Could not parse: " << s << endl;
            return false;
        }

        if (oval >= 256 || nval >= 256)
        {
            cerr << "Invalid byte values: " << s << endl;
            return false;
        }

        Byte bv = { off, patch_byte(oval), patch_byte(nval) };
        entries.push_back(bv);
    }

    if (entries.empty())
    {
        cerr << "No lines recognized." << endl;
        return false;
    }

    return true;
}

BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
    int state = 0;

    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];

        if (bv.offset >= len)
        {
            cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
            return Conflict;
        }

        patch_byte cv = ptr[bv.offset];
        if (bv.old_val == cv)
            state |= Unapplied;
        else if (bv.new_val == cv)
            state |= Applied;
        else
        {
            cerr << std::hex << bv.offset << ": "
                 << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
                 << ", but currently " << unsigned(cv) << std::dec << endl;
            return Conflict;
        }
    }

    return State(state);
}

void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];
        assert (bv.offset < len);

        ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
    }
}

bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "rb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    fseek(f, 0, SEEK_END);
    pvec->resize(ftell(f));
    fseek(f, 0, SEEK_SET);
    size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
    fclose(f);

    return cnt == pvec->size();
}

bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "wb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
    fclose(f);

    return cnt == pvec.size();
}

std::string compute_hash(const std::vector<patch_byte> &pvec)
{
    md5wrapper md5;
    return md5.getHashFromBytes(pvec.data(), pvec.size());
}

int main (int argc, char *argv[])
{
    if (argc <= 3)
    {
        cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
        return 2;
    }

    std::string cmd = argv[1];

    if (cmd != "check" && cmd != "apply" && cmd != "remove")
    {
        cerr << "Invalid command: " << cmd << endl;
        return 2;
    }

    std::string exe_file = argv[2];
    std::vector<patch_byte> bindata;
    if (!load_file(&bindata, exe_file))
        return 2;

    BinaryPatch patch;
    if (!patch.loadDIF(argv[3]))
        return 2;

    BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
    if (state == BinaryPatch::Conflict)
        return 1;

    if (cmd == "check")
    {
        switch (state)
        {
        case BinaryPatch::Unapplied:
            cout << "Currently not applied." << endl;
            break;
        case BinaryPatch::Applied:
            cout << "Currently applied." << endl;
            break;
        case BinaryPatch::Partial:
            cout << "Currently partially applied." << endl;
            break;
        default:
            break;
        }

        return 0;
    }
    else if (cmd == "apply")
    {
        if (state == BinaryPatch::Applied)
        {
            cout << "Already applied." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), true);
    }
    else if (cmd == "remove")
    {
        if (state == BinaryPatch::Unapplied)
        {
            cout << "Already removed." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), false);
    }

    if (!save_file(bindata, exe_file + ".bak"))
    {
        cerr << "Could not create backup." << endl;
        return 1;
    }

    if (!save_file(bindata, exe_file))
        return 1;

    cout << "Patched " << patch.entries.size()
         << " bytes, new hash: " << compute_hash(bindata) << endl;
    return 0;
}


/*
http://example.com

A thread-safe logging console with a line editor for windows.

Based on linenoise win32 port,
All rights reserved.
The original linenoise can be found at: http://example.com

*/

#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <istream>
#include <string>
#include <stdint.h>

#include <cstdio>
#include <cstdlib>
#include <sstream>
#include <vector>

#include <memory>

#include <md5wrapper.h>

using std::cout;
using std::cerr;
using std::endl;

typedef unsigned char patch_byte;

struct BinaryPatch {
    struct Byte {
        unsigned offset;
        patch_byte old_val, new_val;
    };
    enum State {
        Conflict = 0,
        Unapplied = 1,
        Applied = 2,
        Partial = 3
    };

    std::vector<Byte> entries;

    bool loadDIF(std::string name);
    State checkState(const patch_byte *ptr, size_t len);

    void apply(patch_byte *ptr, size_t len, bool newv);
};

inline bool is_hex(char c)
{
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

bool BinaryPatch::loadDIF(std::string name)
{
    entries.clear();

    std::ifstream infile(name);

   if(infile.bad())
    {
        cerr << "Cannot open file: " << name << endl;
        return false;
    }

    std::string s;
    while(std::getline(infile, s))
    {
        // Parse lines that begin with "[0-9a-f]+:"
        size_t idx = s.find(':');
        if (idx == std::string::npos || idx == 0 || idx > 8)
            continue;

        bool ok = true;
        for (size_t i = 0; i < idx; i++)
            if (!is_hex(s[i]))
                ok = false;
        if (!ok)
            continue;

        unsigned off, oval, nval;
        int nchar = 0;
        int cnt = sscanf(s.c_str(), "%x: %x %x%n", &off, &oval, &nval, &nchar);

        if (cnt < 3)
        {
            cerr << "Could not parse: " << s << endl;
            return false;
        }

        for (size_t i = nchar; i < s.size(); i++)
        {
            if (!isspace(s[i]))
            {
                cerr << "Garbage at end of line: " << s << endl;
                return false;
            }
        }

        if (oval >= 256 || nval >= 256)
        {
            cerr << "Invalid byte values: " << s << endl;
            return false;
        }

        Byte bv = { off, patch_byte(oval), patch_byte(nval) };
        entries.push_back(bv);
    }

    if (entries.empty())
    {
        cerr << "No lines recognized." << endl;
        return false;
    }

    return true;
}

BinaryPatch::State BinaryPatch::checkState(const patch_byte *ptr, size_t len)
{
    int state = 0;

    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];

        if (bv.offset >= len)
        {
            cerr << "Offset out of range: 0x" << std::hex << bv.offset << std::dec << endl;
            return Conflict;
        }

        patch_byte cv = ptr[bv.offset];
        if (bv.old_val == cv)
            state |= Unapplied;
        else if (bv.new_val == cv)
            state |= Applied;
        else
        {
            cerr << std::hex << bv.offset << ": "
                 << unsigned(bv.old_val) << " " << unsigned(bv.new_val)
                 << ", but currently " << unsigned(cv) << std::dec << endl;
            return Conflict;
        }
    }

    return State(state);
}

void BinaryPatch::apply(patch_byte *ptr, size_t len, bool newv)
{
    for (size_t i = 0; i < entries.size(); i++)
    {
        Byte &bv = entries[i];
        assert (bv.offset < len);

        ptr[bv.offset] = (newv ? bv.new_val : bv.old_val);
    }
}

bool load_file(std::vector<patch_byte> *pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "rb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    fseek(f, 0, SEEK_END);
    pvec->resize(ftell(f));
    fseek(f, 0, SEEK_SET);
    size_t cnt = fread(pvec->data(), 1, pvec->size(), f);
    fclose(f);

    return cnt == pvec->size();
}

bool save_file(const std::vector<patch_byte> &pvec, std::string fname)
{
    FILE *f = fopen(fname.c_str(), "wb");
    if (!f)
    {
        cerr << "Cannot open file: " << fname << endl;
        return false;
    }

    size_t cnt = fwrite(pvec.data(), 1, pvec.size(), f);
    fclose(f);

    return cnt == pvec.size();
}

std::string compute_hash(const std::vector<patch_byte> &pvec)
{
    md5wrapper md5;
    return md5.getHashFromBytes(pvec.data(), pvec.size());
}

int main (int argc, char *argv[])
{
    if (argc <= 3)
    {
        cerr << "Usage: binpatch check|apply|remove <exe> <patch>" << endl;
        return 2;
    }

    std::string cmd = argv[1];

    if (cmd != "check" && cmd != "apply" && cmd != "remove")
    {
        cerr << "Invalid command: " << cmd << endl;
        return 2;
    }

    std::string exe_file = argv[2];
    std::vector<patch_byte> bindata;
    if (!load_file(&bindata, exe_file))
        return 2;

    BinaryPatch patch;
    if (!patch.loadDIF(argv[3]))
        return 2;

    BinaryPatch::State state = patch.checkState(bindata.data(), bindata.size());
    if (state == BinaryPatch::Conflict)
        return 1;

    if (cmd == "check")
    {
        switch (state)
        {
        case BinaryPatch::Unapplied:
            cout << "Currently not applied." << endl;
            break;
        case BinaryPatch::Applied:
            cout << "Currently applied." << endl;
            break;
        case BinaryPatch::Partial:
            cout << "Currently partially applied." << endl;
            break;
        default:
            break;
        }

        return 0;
    }
    else if (cmd == "apply")
    {
        if (state == BinaryPatch::Applied)
        {
            cout << "Already applied." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), true);
    }
    else if (cmd == "remove")
    {
        if (state == BinaryPatch::Unapplied)
        {
            cout << "Already removed." << endl;
            return 0;
        }

        patch.apply(bindata.data(), bindata.size(), false);
    }

    if (!save_file(bindata, exe_file + ".bak"))
    {
        cerr << "Could not create backup." << endl;
        return 1;
    }

    if (!save_file(bindata, exe_file))
        return 1;

    cout << "Patched " << patch.entries.size()
         << " bytes, new hash: " << compute_hash(bindata) << endl;
    return 0;
}


TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])


{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)

  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();

  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non

  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)


if (data[0] != expected_value) bad.push_back(i);

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {

    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()
   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}


/*
// @HEADER
//          Tpetra: Templated Linear Algebra Services Package
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
// @HEADER
*/
#include <numeric>
#include <Tpetra_Map.hpp>
#include <Tpetra_Core.hpp>
#include <Tpetra_MultiVector.hpp>
#include "Tpetra_TestingUtilities.hpp"

#include <Teuchos_Array.hpp>
#include <Teuchos_CommHelpers.hpp>
#include <Teuchos_Tuple.hpp>

#include "TpetraCore_ETIHelperMacros.h"

namespace {

using Tpetra::TestingUtilities::getDefaultComm;

TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL(Directory, AllMinGIDs, SC, LO, GO)
{
  /*
   * This issue is described in
   * [Issue 6987](http://example.com)
   * and summarized here
   *
   * Map::myMinGID_ is the minimum GID held by a map on its owning process. For
   * a map that has no entries on a process, its value is std::numeric_limits<GO>::max()
   *
   * Directory::allMinGIDs_ array holds the minimum GID on each participating
   * process. This array is used, eg, in the directory's getEntriesImpl method.
   * For ranks whose maps are empty allMinGIDs_[rank] is std::numeric_limits<GO>::max()

   * and any work using it violates the assumptions of the search algorithm that assumes
   * the GIDs are uniformly distributed and monotonically increasing
   *
   * @rppawlo discovered this in a simple test wherein a map was empty on every
   * process except the last. That map is used to build a MultiVector and then
   * import that MultiVector to another MultiVector with a locally replicated
   * map. The result is failure to import the values from the original vector to
   * the locally replicated vector (because of comparisons with uninitialized
   * values in getEntriesImpl.
   */

  using map_type = Tpetra::Map<LO, GO>;
  using vector_type = Tpetra::MultiVector<SC, LO, GO>;
  using import_type = Tpetra::Import<LO, GO>;

  auto comm = getDefaultComm();
  const auto my_rank = comm->getRank();
  const auto num_procs = comm->getSize();

  const GO index_base = 0;
  const size_t num_vecs = 1;

  const size_t num_non_zero = 5;
  const SC expected_value = Teuchos::as<SC>(5.0);
  std::vector<int> non_empty_ranks(num_procs);
  std::iota(non_empty_ranks.begin(), non_empty_ranks.end(), 0);
  for (auto && non_empty_rank : non_empty_ranks)
  {

    Teuchos::RCP<vector_type> vec1;
    Teuchos::RCP<map_type> map1;
    {
      const auto num_global_elements = Teuchos::as<Tpetra::global_size_t>(num_non_zero);
      const size_t num_local_elements = (my_rank == non_empty_rank) ? num_non_zero : 0;
      map1 = Teuchos::rcp(new map_type(num_global_elements, num_local_elements, index_base, comm));
      vec1 = Teuchos::rcp(new vector_type(map1, num_vecs, true));
      if (my_rank == non_empty_rank) vec1->putScalar(expected_value);
    }

    Teuchos::RCP<vector_type> vec2;
    Teuchos::RCP<const map_type> map2;
    {
      map2 = Tpetra::createLocalMap<LO,GO>(num_non_zero, comm);
      vec2 = Teuchos::rcp(new vector_type(map2, num_vecs, true));
    }

    auto import = import_type(map1, map2);
    vec2->doImport(*vec1, import, Tpetra::INSERT);

    auto data = vec2->getData(0);
    TEUCHOS_TEST_FOR_EXCEPTION(
      data.size() != num_non_zero,
      std::logic_error,
      "Vector data.size should be " << num_non_zero << " but is " << data.size()
    );

    std::vector<size_t> bad;
    for (size_t i=0; i<num_non_zero; i++)
    {
      if (data[0] != expected_value) bad.push_back(i);
    }

    if (bad.size() > 0)
    {
      out << "The following vector entries are incorrect after import:\n";
      for (auto && i : bad)
        out << "data[" << i << "] = " << data[i] << " != " << expected_value << "\n";
      TEST_ASSERT(false);
    }
  }
}

// INSTANTIATIONS

#define THIS_TEST_GROUP(SC, LO, GO) \
  TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT(Directory, AllMinGIDs, SC, LO, GO)

TPETRA_ETI_MANGLING_TYPEDEFS()
TPETRA_INSTANTIATE_SLG(THIS_TEST_GROUP)

} // namespace (anonymous)

int
main (int argc, char* argv[])
{
  Tpetra::ScopeGuard tpetra_scope(&argc, &argv);
  const int err_code =
    Teuchos::UnitTestRepository::runUnitTestsFromMain(argc, argv);
  return err_code;
}