#include <string>
#include <vector>
#include <cstdarg>
#include <iostream>

// to do:
// match
// sub
// gsub
// split_re

// Splitting strings
std::vector<std::string> split (std::string delim, std::string input) {
    std::vector<std::string> result;
    int dpos = 0, dlen = delim.length ();
    for (int begin = 0; dpos != std::string::npos; begin = dpos + dlen) {
        dpos = input.find (delim, begin);
        if (dpos == std::string::npos) {
		    result.push_back (input.substr (begin));
        } else {
            result.push_back (input.substr (begin, dpos - begin));
        }
    }
    // Remove trailing final fields
    while ((!result.empty ()) && (result.back ().compare ("") == 0))
        result.pop_back ();
	return result;
}

// Printing lists of strings
std::string default_list_delimiter (",");
namespace std { 
std::ostream &std::operator <<(std::ostream& out, const std::vector<std::string> &vec) {
    for (std::vector<std::string>::const_iterator i = vec.begin (), e = vec.end (); i != e; ++i) {
        if (i != vec.begin ()) out << default_list_delimiter;
        out << *i;
    }
    return out;
}
}

// Joining lists of strings
std::string join (std::string delim, std::vector<std::string> vec) {
    std::string result;
    for (std::vector<std::string>::const_iterator i = vec.begin (), e = vec.end (); i != e; ++i) {
        if (i != vec.begin ()) result.append (delim);
        result.append (i->c_str ());
    }
    return result;
}

// Getting output from another process
std::string output_from (std::string command) {
	FILE *cmdpipe = popen(command.c_str(), "r");
	std::string result;
	char stuff[1024];
	if (!cmdpipe)
		return "";
	size_t count;
    do {
	    count = fread(stuff, 1, 1023, cmdpipe);
	    stuff[count] = '\0';
		result.append(stuff);
	} while (!((count < 1023) || feof(cmdpipe) || ferror(cmdpipe)));
	pclose(cmdpipe);
	return result;
}

// Testing
static int failures = 0;

std::vector<std::string> vmake_vect(int count, va_list ap) {
    std::vector<std::string> result;
    for (int j = 0; j < count; ++j) {
        char *s = va_arg(ap, char *);
        result.push_back(s);
    }
    return result;
}

std::vector<std::string> make_vect(int count, ...) {
    va_list ap;
    std::vector<std::string> result;
    va_start (ap, count);
    result = vmake_vect(count, ap);
    va_end (ap);
    return result;
}

template<class T>
bool equal_vect(std::vector<T> a, std::vector<T> b) {
    typedef typename std::vector<T>::iterator vec_iter;
	if (a.size() != b.size()) return false;
	for (vec_iter ai = a.begin(), bi = b.begin(),
			ae = a.end(), be = b.end(); ai != ae; ++ai, ++bi) {
		if (*ai != *bi) return false;
	}
	return true;
}

void test_split(std::vector<std::string> result, char *delim, char *input) {
    std::vector<std::string> actualResult;
    actualResult = split(delim, input);
	if (!equal_vect(result, actualResult)) {
		std::cerr << "FAIL: split(\"" << delim << "\", \"" << input
			      << "\") = (" << result << ") (length = " << result.size ()
				  << ")\n";
		++failures;
	}
}

void test_join(std::string result, char *delim, int count, ...) {
	va_list ap;
	va_start(ap, count);
	std::vector<std::string> vec = vmake_vect(count, ap);
	std::string actualResult;
	actualResult = join(delim, vec);
	if (actualResult.compare(result) != 0) {
		std::cerr << "FAIL: join(\"" << delim << "\", (" << vec << ")) = \""
                  << actualResult << "\"\n";
		++failures;
	}
	va_end(ap);
}

void test_output_from(std::string result, std::string command) {
	std::string actualResult;
	actualResult = output_from(command);
	if (actualResult.compare(result) != 0) {
		std::cerr << "FAIL: output_from(\"" << command << "\") = \""
                  << actualResult << "\"\n";
		++failures;
	}
}


int main(int argc, char **argv) {
	std::vector<std::string> resVec;
	std::string resStr;

	resVec = make_vect(2,"a","b");        test_split(resVec,"+","a+b");
	resVec = make_vect(3,"","a","b");     test_split(resVec,"x","xaxb");
	resVec = make_vect(2,"a","b");        test_split(resVec,"-=","a-=b-=");
	resVec = make_vect(4,"","1","2","3"); test_split(resVec,"!","!1!2!3!!");

	resStr = "axb";          test_join(resStr, "x", 2, "a", "b");
	resStr = "abc";          test_join(resStr, "", 3, "a", "b", "c");
	resStr = "";             test_join(resStr, "", 0);
	resStr = "xyz";          test_join(resStr, ",", 1, "xyz");
	resStr = "eks,why,zee";  test_join(resStr, ",", 3, "eks", "why", "zee");
	resStr = "eks,why,zee,"; test_join(resStr, ",", 4, "eks", "why", "zee", "");
	resStr = ",why,zee,eks"; test_join(resStr, ",", 4, "", "why", "zee", "eks");

	resStr = "foo\n";      test_output_from(resStr, "echo foo");
	resStr = "foo\nbar\n"; test_output_from(resStr, "echo foo; echo bar");
	resStr = "4\n";        test_output_from(resStr, "expr 2 + 2");

	if (failures) {
		std::cerr << failures << " failures\n";
		return 1;
	} else {
		std::cout << "No failures\n";
		return 0;
	}
}
