libcompress
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © pro1007ka

//@version=5

// @description numbers compressor for large output data compression
library("libcompress")


__int2bin(int n) => 
	ret = ""

	if n == 0
		ret := "0"
	else
		nc = n
		while nc > 0
			ret := str.tostring(nc % 2) + ret
			nc := int(nc/2)
	
	ret


__int2bin(int n, int padding) =>
	ret = __int2bin(n)

	while str.length(ret) < padding
		ret := '0' + ret

	ret


__first_nonzero_pos(string s) =>
	ret = -1

	if(str.length(s))
		for i = 0 to str.length(s) - 1 by 1
			if str.substring(s, i, i+1) != '0'
				ret := i
				break
	
	ret


__calc_exponent(float x) =>
	s = str.tostring(x)
	if str.contains(s, '.')
		vals = str.split(s, '.')
		lval = array.get(vals, 0)
		rval = array.get(vals, 1)
		if lval == "0"
			- 1 - __first_nonzero_pos(rval)
		else
			str.length(array.get(str.split(s, '.'), 0)) - 1
	else
		str.length(s) - 1


var string base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


__int2base64digit(int n) =>
	str.substring("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", n, n+1)

__baset64digit2int(string d) =>
	str.pos("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", d)

__float_erase_point(float x) =>
	str.replace_all(str.tostring(x), '.', '')


__calc_mantissa(int arg, int size) =>
	argcpy = arg
	//while str.length(__int2bin(argcpy)) > size
	argmax = math.pow(2, size)
	while argcpy > argmax
		argcpy := int(argcpy / 10)

	ret = __int2bin(argcpy)
	
	while str.length(ret) < size
		ret := '0' + ret

	ret



__6bits2int(string bits) =>
	ret = 0
	__exp = 32
	for i = 0 to 5
		ret := ret + __exp * int(str.tonumber(str.substring(bits, i, i + 1)))
		__exp := int(__exp/2)

	ret


__bin2base64(string bin) =>
	bin_raw = bin
	while str.length(bin_raw) % 6 != 0
		bin_raw := "0" + bin_raw

	string ret = ""
	
	for i = 0 to str.length(bin_raw) -1 by 6
		ret := ret + __int2base64digit(__6bits2int(str.substring(bin_raw, i, i + 6)))

	ret

// @function converts float to base64 (4 chars) | 24 bits: 1 sign + 5 exponent + 18 mantissa
// @returns 4-character base64_1/5/18 representation of x
export compress_fp24(float x) =>
	sign_val = x > 0 ? 32 : 0
	exponent = __calc_exponent(x) + 15

	firstdigit = __int2base64digit(sign_val + exponent)

	mantissa = __calc_mantissa(int(str.tonumber(__float_erase_point(math.abs(x)))), 18)

	firstdigit + __bin2base64(mantissa)


// @function converts unsigned float to base64 (3 chars) | 18 bits: 5 exponent + 13 mantissa
// @returns 3-character base64_0/5/13 representation of x
export compress_ufp18(float x) =>
	exponent = __calc_exponent(x) + 15
	mantissa = __calc_mantissa(int(str.tonumber(__float_erase_point(math.abs(x)))), 13)

	ret_bin = __int2bin(exponent, 5) + mantissa

	__bin2base64(ret_bin)


// @function converts int to base64
export compress_int(int n) =>
	__bin2base64(__int2bin(n))


// @function converts base64 to int string
export base64_tointstr(string b64, int fsize) =>
	ret = 0
	for _i = 0 to str.length(b64) - 1
		i = str.length(b64) - 1 - _i
		ret := ret + int(math.pow(64, i)) * __baset64digit2int(str.substring(b64, i, i + 1))

	retstr = str.tostring(ret)
	
	while str.length(retstr) < fsize
		retstr := "0" + retstr
		
	retstr