*! version 1.0.0 Will Matsuoka 01jan2016
* http://www.wmatsuoka.com

program define encode64, rclass
	mata: st_local("encode64", encode64(`"`1'"'))
	return local base64 "`encode64'"

	di "`encode64'"
end

program define decode64, rclass
	mata: st_local("decode64", decode64(`"`1'"'))
	return local base64 "`decode64'"

	di "`decode64'"
end

mata:
string scalar encode64(string scalar x)
{
	real scalar    endsub, len
	real matrix    base2
	string matrix  y
	string scalar  digits, output

	encode64_padding(x, endsub)

	len = strlen(x)/6
	y = J(1, len, "")
	for(i=1; i<=len; i++) {
		y[i] = substr(x, (i-1)*6 + 1, 6)
	}

	base2  = frombase(2, y')
	digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

	output = ""
	for (i=1; i<=length(base2); i++) {
		output = output + substr(digits, base2[i] + 1, 1)
	}
	return(substr(output, 1, strlen(output) - endsub) + "=" * endsub)
}
void encode64_padding(string scalar x, real scalar endsub)
{
	real scalar    numpad
	real scalar    padding

	padding = strlen(x)
	if ((numpad=mod(padding, 3))==2) {
		x = x + char(0)
		endsub = 1
	}
	else if (numpad==1) {
		x = x + 2*char(0)
		endsub = 2
	}
	else endsub = 0

	x = inbase(2, ascii(x))
	for (i=1; i<=length(x); i++) {
		x[i] = "0" * (8-strlen(x[i])) + x[i]
	}
	x = invtokens(x, "")
}

string scalar decode64(string scalar x)
{
	real scalar    x_len, base2_len
	real matrix    y
	string scalar  digits
	string matrix  output, buf, base2

	digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

	x_len  = strlen(x)
	output = J(1, x_len, "")
	for (i=1; i<=x_len; i++) {
		output[i] = substr(x, i, 1)
	}

	y = strpos(digits, output) :- 1
	output = inbase(2, y) :* (inbase(2, y) :!= "-1")

	buf   = "0" :* (6 :- strlen(output))
	base2 = buf :+ output
	base2 = invtokens(base2, "")

	base2_len = strlen(base2) / 8
	output    = J(1, base2_len, "")

	for (i=1; i<=base2_len; i++) {
		output[i] = substr(base2, (i-1) * 8 + 1, 8)
	}

	output = frombase(2, output)
	return(char(select(output, output)))
}
end