/**
 * @author KyMAN
 * @version 20241105
 */

/**
 * @callback anyanka_keys_default_callback
 * @returns {void}
 */

/**
 * @callback anyanka_keys_convert_callback
 * @param {!number} value 
 * @returns {void}
 */

/**
 * @callback anyanka_keys_data_callback
 * @param {!string} data 
 * @param {!number} length 
 * @returns {void}
 */

/**
 * @callback anyanka_keys_value_callback
 * @param {!number} value 
 * @param {!number} i 
 * @returns {void}
 */

/**
 * @class
 * @constructor
 * @param {Object.<string, any|null>} [inputs = {}] 
 * @returns {void}
 * @access public
 */
export const AnyankaKeys = (function(){
    
    /**
     * @constructs AnyankaKeys
     * @param {Object.<string, any|null>} [inputs = {}] 
     * @returns {void}
     * @access private
     */
    const AnyankaKeys = function(inputs = {}){

        /** @type {AnyankaKeys} */
        const self = this, 
            /** @type {Array.<number>} */
            public_key = [], 
            /** @type {Array.<number>} */
            private_key = [], 
            /** @type {AnyankaKeys.Options} */
            options = new AnyankaKeys.Options(inputs);
        /** @type {string} */
        let alphabet = AnyankaKeys.SETTINGS.alpahbet, 
            /** @type {number} */
            base_from = AnyankaKeys.SETTINGS.base_from, 
            /** @type {number} */
            base_to = AnyankaKeys.SETTINGS.base_to, 
            /** @type {number} */
            divisor = AnyankaKeys.SETTINGS.divisor, 
            /** @type {number} */
            public_key_length = AnyankaKeys.SETTINGS.public_key_length, 
            /** @type {number} */
            private_key_length = AnyankaKeys.SETTINGS.private_key_length, 
            /** @type {number} */
            segments_key_length = AnyankaKeys.SETTINGS.segments_key_length, 
            /** @type {number|boolean|null} */
            summatory_value = AnyankaKeys.SETTINGS.summatory, 
            /** @type {boolean} */
            has_incremental = AnyankaKeys.SETTINGS.incremental, 
            /** @type {boolean|number|null} */
            random_private_start = AnyankaKeys.SETTINGS.random_private_start, 
            /** @type {number} */
            hash_length = AnyankaKeys.SETTINGS.hash_length, 
            /** @type {number} */
            hash_power = AnyankaKeys.SETTINGS.hash_power;

        /**
         * @returns {void}
         * @access private
         */
        const constructor = () => {

            if(inputs){
                if(typeof inputs == "string")
                    inputs = {alphabet : inputs};
                else if(!AnyankaKeys.is_dictionary(inputs))
                    inputs = {};
            }else 
                inputs = {};

            inputs.alphabet !== undefined && (alphabet = inputs.alphabet);
            inputs.base_from !== undefined && (base_from = inputs.base_from);
            inputs.base_to !== undefined && (base_to = inputs.base_to);
            inputs.public_key_length !== undefined && (public_key_length = inputs.public_key_length);
            inputs.private_key_length !== undefined && (private_key_length = inputs.private_key_length);
            inputs.segments_key_length !== undefined && (segments_key_length = inputs.segments_key_length);
            inputs.summatory !== undefined && (summatory_value = inputs.summatory);
            inputs.incremental !== undefined && (has_incremental = inputs.incremental);
            inputs.random_private_start !== undefined && (random_private_start = inputs.random_private_start);
            inputs.hash_length !== undefined && (hash_length = inputs.hash_length);
            inputs.hash_power !== undefined && (hash_power = inputs.hash_power);

            (
                typeof (alphabet = inputs.alphabet) != "string" || 
                (alphabet = [...alphabet].filter((character, index) => alphabet.indexOf(character) == index)).length < 1
            ) && (alphabet = AnyankaKeys.SETTINGS.alphabet);
            (
                typeof (base_from = inputs.base_from) != "number" || 
                base_from != base_from >> 0 || 
                base_from < 2
            ) && (base_from = AnyankaKeys.SETTINGS.base_from);
            (
                typeof (base_to = inputs.base_to) != "number" || 
                base_to != base_to >> 0 || 
                base_to < 2 || 
                base_to >= alphabet.length
            ) && (base_to = AnyankaKeys.SETTINGS.base_to);

            self.set_public_key(true);

            typeof summatory_value != "number" && summatory_value !== true && summatory_value !== null && (summatory_value = AnyankaKeys.SETTINGS.summatory);
            typeof has_incremental != "boolean" && (has_incremental = AnyankaKeys.SETTINGS.incremental);
            typeof random_private_start != "number" && random_private_start !== true && random_private_start !== null && (random_private_start = AnyankaKeys.SETTINGS.random_private_start);
            typeof hash_length != "number" && (hash_length = AnyankaKeys.SETTINGS.hash_length);
            typeof hash_power != "number" && (hash_power = AnyankaKeys.SETTINGS.hash_power);

        };

        /**
         * @returns {string|null}
         * @access public
         */
        this.get_public_key = () => public_key.length ? public_key.map(key => alphabet[key]).join("") : null;

        /**
         * @param {!number} x 
         * @returns {number}
         * @access private
         */
        const next = x => {

            x += 1 + divisor;
            while(x >= 2)
                x --;

            return x;
        };

        /**
         * @param {string|boolean|null} [new_key = null] 
         * @returns {number}
         * @access public
         */
        this.set_public_key = (new_key = null) => {

            /** @type {number} */
            let error = 0;

            if(new_key === undefined)
                error |= 1 << 1;
            else{
                if(new_key === null){
                    AnyankaKeys.reset_array_with(public_key);
                    AnyankaKeys.reset_array_with(private_key, [0]);
                }else{

                    /** @type {Array.<number>|null} */
                    let new_public_key = null;

                    switch(typeof new_key){
                        case "boolean":
                            if(new_key)
                                new_public_key = Array.from({
                                    length : public_key_length
                                }, () => Math.random() * base_to >> 0);
                            else 
                                error |= 1 << 4;
                        break;
                        case "string":
                            new_public_key = [...new_key].map(character => alphabet.indexOf(character));
                            if(Math.min(new_public_key) == -1)
                                error |= 1 << 5;
                        break;
                        default:
                            error |= 1 << 3;
                        break;
                    };

                    if(!error){

                        AnyankaKeys.reset_array_with(public_key, new_public_key);

                        /** @type {number} */
                        const segment = public_key.length / segments_key_length >> 0, 
                            /** @type {number} */
                            start = Number(("" + divisor).split(".")[1]) % base_to;
                        /** @type {number} */
                        let x = 1;

                        public_key.length && AnyankaKeys.reset_array_with(private_key, Array.from({
                            length : private_key_length
                        }, (_, i) => (AnyankaKeys.summatory(
                            segments_key_length, 
                            j => public_key[(j * segment + i) % public_key_length], 
                            start, 
                            base_to
                        ) * (x = next(x)) >> 0) % base_to));

                    };

                };
            };

            return error;
        };

        /**
         * @param {!number} i 
         * @param {!number} value 
         * @returns {string}
         * @access private
         */
        const set_key = (i, value) => alphabet[(value + private_key[i % private_key_length]) % base_to];

        /**
         * @param {!(any|null)} data 
         * @returns {string}
         * @access public
         */
        this.encrypt = data => {

            /** @type {string} */
            let header = "", 
                /** @type {string} */
                body = "", 
                /** @type {number} */
                pre_empties = 0, 
                /** @type {number} */
                i = 0;
            /** @type {AnyankaKeys.BaseChange.Parameters} */
            const parameters = new AnyankaKeys.BaseChange.Parameters({
                    /** @type {number} */
                    from : base_from, 
                    /** @type {number} */
                    to : base_to
                }), 
                /** @type {Array.<number>} */
                header_data = (
                    random_private_start === true ? Array.from({length : 2}, () => Math.random() * base_to >> 0) : 
                    typeof random_private_start == "number" ? Array.from([base_to, 1], i => (random_private_start / i >> 0) % base_to) : 
                [0, 0]), 
                /** @type {number} */
                summatory = (summatory_value === true ? Math.random() * base_to >> 0 : summatory_value % 58) || 1;
                /** @type {number} */
                private_start = (header_data[0] * base_to + header_data[1]) % private_key_length, 
                /** @type {anyanka_keys_convert_callback} */
                set = value => body = set_key(private_start + (has_incremental ? i ++ : 1) * summatory, value) + body, 
                /** @type {AnyankaKeys.Type} */
                type = AnyankaKeys.get_each_bytes(data, byte => AnyankaKeys.change_base_iteration(byte, set, parameters), options);

            if([5, 6, 7, 8].includes(type.code))
                data.some(value => {
                    if(value)
                        return true;
                    pre_empties ++;
                });
            else if(type.code == 10)
                pre_empties = type.empties;

            parameters.end(set);
            header += alphabet[header_data[0]] + alphabet[header_data[1]] + set_key(private_start, summatory);
            [type.code, pre_empties].forEach((value, i) => header += set_key(private_start + (has_incremental ? i : 1) * summatory, value));

            return header + body;
        };

        /**
         * @param {!number} i 
         * @param {!string} character 
         * @returns {number}
         * @access private
         */
        const get_key = (i, character) => {
            
            /** @type {number} */
            let value = alphabet.indexOf(character) - private_key[i % private_key_length];

            return value < 0 ? value + base_to : value;
        };

        /**
         * @param {!string} data 
         * @returns {any|null}
         * @access public
         */
        this.decrypt = data => {

            /** @type {string} */
            const header = data.substring(0, 5), 
                /** @type {AnyankaKeys.BaseChange.Parameters} */
                parameters = new AnyankaKeys.BaseChange.Parameters({
                    /** @type {number} */
                    from : base_to, 
                    /** @type {number} */
                    to : base_from
                }), 
                /** @type {string} */
                body = data.substring(5), 
                /** @type {number} */
                body_l = body.length - 1, 
                /** @type {number} */
                private_start = (alphabet.indexOf(header[0]) * base_to + alphabet.indexOf(header[1])) % private_key_length, 
                /** @type {number} */
                summatory = get_key(private_start, header[2]), 
                /** @type {AnyankaKeys.Type} */
                type = new AnyankaKeys.Type(get_key(
                    private_start + (has_incremental ? 0 : summatory), header[3]
                ), body, parameters.get_size(body), options);
            /** @type {number} */
            let pre_empties = get_key(private_start + summatory, header[4]);

            [...body].forEach((character, i) => AnyankaKeys.change_base_iteration(get_key(
                private_start + (has_incremental ? body_l - i : 1) * summatory, character
            ), type.set_value, parameters));
            parameters.end(type.set_value);
            (pre_empties == type.length() ? -- pre_empties : pre_empties) > 0 && type.set_pre_empties(pre_empties);
            type.build();

            return type.data;
        };

        /**
         * @param {any|null} data 
         * @returns {string}
         * @access public
         */
        this.hash = data => {

            /** @type {number} */
            let summatory = 1, 
                /** @type {number} */
                i = 0, 
                /** @type {number} */
                x = 1, 
                /** @type {number} */
                wise = 0;
            /** @type {Array.<number>} */
            const hash = Array.from({length : hash_length}, () => 0), 
                /** @type {number} */
                value_limit = base_to ** hash_power, 
                /** @type {AnyankaKeys.Type} */
                type = AnyankaKeys.get_each_bytes(data, byte => {

                    /** @type {number} */
                    const k = i ++ % hash_length;

                    summatory = (summatory + byte) % value_limit;
                    hash[k] = (hash[k] + byte) % value_limit;

                }, new AnyankaKeys.Options()), 
                /** @type {number} */
                base = summatory ** divisor / ++ i ** divisor;

            hash[0] += type.code;

            return hash.map(value => {

                value = wise ** (x = next(x)) + (base * value) ** x + base ** x;
                while((value *= base_to) < value_limit);
                wise += (value = (value >> 0) % base_to);

                return alphabet[value];
            }).join("");
        };

        constructor();
        
    };

    /** @type {Object.<string, string|number|boolean|null>} */
    AnyankaKeys.SETTINGS = {
        /** @type {string} */
        alphabet : "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", 
        /** @type {number} */
        base_to : 58, // Cast for unique alphabet characters.
        /** @type {number} */
        base_from : 256, 
        /** @type {boolean|string|null} */
        public_key : true, // true = Random || null = No keys || String = Default key
        /** @type {number} */
        public_key_length : 1 << 8, 
        /** @type {number} */
        private_key_length : 1 << 12, 
        /** @type {number} */
        segments_key_length : 13, 
        /** @type {boolean|number|null} */
        summatory : true, // true = Random || null = No summatory || number = Default summatory
        /** @type {boolean} */
        incremental : true, 
        /** @type {boolean|number|null} */
        random_private_start : true, 
        /** @type {number} */
        divisor : .21417, 
        /** @type {number} */
        hash_length : 47, 
        /** @type {number} */
        hash_power : 1.89673, 
        /** @type {number} */
        scape_character : 92, // Character ["\\", 92]
        /** @type {number} */
        scape_blocks : 2 // Bits: [UTF-8, UTF-16, URI, Force UTF-16]
    };

    /**
     * @param {!number} length 
     * @param {!anyanka_keys_convert_callback} item_callback 
     * @returns {void}
     * @access public
     * @static
     */
    AnyankaKeys.bucle = (length, item_callback) => {

        /** @type {number} */
        let i = 0;

        while(i < length)
            item_callback(i ++);

    };

    /**
     * @param {!number} length 
     * @param {!anyanka_keys_convert_callback} callback 
     * @param {!number} [start = 0] 
     * @param {?number} [limit = null]
     * @returns {number}
     * @access public
     * @static
     */
    AnyankaKeys.summatory = (length, callback, start = 0, limit = null) => {

        limit || (limit = ~-(1 << 31));

        AnyankaKeys.bucle(length, i => start = (start + callback(i)) % limit);

        return start;
    };

    /**
     * @param {!Array.<any|null>} array 
     * @param {Array.<any|null>} [new_data = []] 
     * @returns {void}
     * @access public
     * @static
     */
    AnyankaKeys.reset_array_with = (array, new_data = []) => {
        array.splice(0, array.length, ...new_data);
    };

    /**
     * @constructor
     * @param {!Object.<string, number>} [inputs = {}] 
     * @returns {void}
     * @access public
     * @static
     */
    AnyankaKeys.Options = function(inputs = {}){

        /** @type {number} */
        this.scape_blocks = (
            inputs.scape_blocks !== undefined && 
            typeof inputs.scape_blocks == "number" && 
            inputs.scape_blocks >= 0 && 
            inputs.scape_blocks <= 4
        ) ? inputs.scape_blocks : AnyankaKeys.SETTINGS.scape_blocks;
        /** @type {number} */
        this.scape_character = (
            inputs.scape_character !== undefined && 
            typeof inputs.scape_character == "number" && 
            inputs.scape_character & 0xFF == inputs.scape_character
        ) ? inputs.scape_character : AnyankaKeys.SETTINGS.scape_character;

        /** @type {boolean} */
        this.scape_utf8 = !!(this.scape_blocks & (1 << 0));
        /** @type {boolean} */
        this.scape_utf16 = !!(this.scape_blocks & (1 << 1));
        /** @type {boolean} */
        this.scape_uri = !!(this.scape_blocks & (1 << 2));
        /** @type {boolean} */
        this.force_utf16 = !!(this.scape_blocks & (1 << 3));
        /** @type {boolean} */
        this.scape = this.scape_utf8 || this.scape_utf16;

    };

    /**
     * @constructor
     * @param {string|number} [name = "unknown"] 
     * @param {string|null} [data = null] 
     * @param {number} [length = 0] 
     * @param {?AnyankaKeys.Options} [options = null] 
     * @returns {void}
     * @access public
     */
    AnyankaKeys.Type = function(name = "unknown", data = null, length = 0, options = null){

        /** @type {AnyankaKeys.Type} */
        const self = this;
        /** @type {any|null} */
        let cache = null, 
            /** @type {Array.<number>|null} */
            string_cache = null;

        /** @type {number} */
        this.code = 0;
        /** @type {string} */
        this.name = name;
        /** @type {any|null} */
        this.data = null;
        /** @type {number} */
        this.empties = 0;

        /**
         * @returns {void}
         * @access private
         */
        const constructor = () => {

            if(typeof name == "number")
                self.name = AnyankaKeys.Type.TYPES[self.code = name];
            else if(name){
                if(self.code = AnyankaKeys.Type.TYPES.indexOf(name = name.toLowerCase()) != -1)
                    self.name = name;
                else 
                    self.code = 0;
            };

            data !== null && self.start(data, length);
            options || (options = new AnyankaKeys.Options());

        };

        /**
         * @param {!number} code 
         * @param {?name} [name = null] 
         * @returns {void}
         * @access public
         */
        this.set = (code, name = null) => {
            self.code = code;
            name && (self.name = name);
        };

        /**
         * @param {!number} byte 
         * @returns {string}
         * @access private
         */
        const get_character_by_byte = (byte) => {

            /** @type {string} */
            let character = "", 
                /** @type {boolean} */
                force_scape = false;

            if(options.scape){
                if(string_cache === null){
                    if(byte == options.scape_character)
                        string_cache = [];
                    else 
                        character = String.fromCharCode(byte);
                }else if(byte == options.scape_character){
                    if(!string_cache.length)
                        character = String.fromCharCode(byte);
                    else{

                        /** @type {number} */
                        let code = 0;
                        
                        string_cache.forEach((byte, i) => code += byte << i * 8);
                        character = String.fromCharCode(code);
                        force_scape = true;
                        string_cache = [];

                    };
                }else if(!string_cache.length){
                    if(options.scape_utf16)
                        string_cache.push(byte);
                    else 
                        character = String.fromCharCode(string_cache[0]);
                }else 
                    character = String.fromCharCode(string_cache[0] | (byte << 8));
            }else 
                character = String.fromCharCode(byte);

            string_cache !== null && !force_scape && character !== "" && (string_cache = null);

            return character;
        };

        /** @type {Object.<string, Object.<string, anyanka_keys_data_callback>|Object.<string, anyanka_keys_value_callback>|Object.<string, anyanka_keys_convert_callback>|Object.<string, anyanka_keys_default_callback>>} */
        this.action = {
            /** @type {Object.<string, anyanka_keys_data_callback>} */
            start : {
                unknown : (data, length) => {}, 
                string : (data, length) => self.data = "", 
                undefined : (data, length) => self.data = undefined, 
                null : (data, length) => {}, 
                json : (data, length) => cache = "", 
                utf8 : (data, length) => cache = [], 
                utf16 : (data, length) => cache = [], 
                utf32 : (data, length) => cache = [], 
                bytes : (data, length) => self.data = [], 
                bool : (data, length) => {}, 
                integer : (data, length) => self.data = 0, 
                float : (data, length) => {}, 
                custom : (data, length) => cache = []
            }, 
            /** @type {Object.<string, anyanka_keys_value_callback>} */
            set_value : {
                unknown : (value, i) => {}, 
                string : (value, i) => self.data = get_character_by_byte(value) + self.data, 
                undefined : (value, i) => {}, 
                null : (value, i) => {}, 
                json : (value, i) => cache = get_character_by_byte(value) + cache, 
                utf8 : (value, i) => cache.unshift(value), 
                utf16 : (value, i) => {
                    if(i % 2)
                        cache[0] = (cache[0] << 8) | value;
                    else 
                        cache.unshift(value);
                }, 
                utf32 : (value, i) => {
                    if(i % 4)
                        cache[0] = (cache[0] << 8) | value;
                    else 
                        cache.unshift(value);
                }, 
                bytes : (value, i) => self.data.unshift(value), 
                bool : (value, i) => self.data = (
                    i ? null : 
                    value === 1 ? true : 
                    value === 0 ? false : 
                null), 
                integer : (value, i) => self.data = (self.data << 8) | value, 
                float : (value, i) => {}, 
                custom : (value, i) => cache.push(value)
            }, 
            /** @type {Object.<string, anyanka_keys_convert_callback>} */
            set_pre_empties : {
                unknown : empties => {}, 
                string : empties => {}, 
                undefined : empties => {}, 
                null : empties => {}, 
                json : empties => {}, 
                utf8 : empties => AnyankaKeys.bucle(empties, () => cache.unshift(0)), 
                utf16 : empties => AnyankaKeys.bucle(empties, () => cache.unshift(0)), 
                utf32 : empties => AnyankaKeys.bucle(empties, () => cache.unshift(0)), 
                bytes : empties => AnyankaKeys.bucle(empties, () => self.data.unshift(0)), 
                bool : empties => {}, 
                integer : empties => self.data <<= empties * 8, 
                float : empties => {}, 
                custom : empties => AnyankaKeys.bucle(empties, () => self.data.unshift(0))
            }, 
            /** @type {Object.<string, anyanka_keys_default_callback>} */
            build : {
                unknown : () => {}, 
                string : () => {
                    string_cache !== null && (self.data = String.fromCharCode(0) + self.data);
                    options.scape_uri && (self.data = decodeURIComponent(self.data));
                }, 
                undefined : () => {}, 
                null : () => {}, 
                json : () => {
                    string_cache !== null && (cache = String.fromCharCode(0) + cache);
                    self.data = JSON.parse(cache ? options.scape_uri ? decodeURIComponent(cache) : cache : "[]");
                }, 
                utf8 : () => self.data = new Uint8Array(cache), 
                utf16 : () => self.data = new Uint16Array(cache), 
                utf32 : () => self.data = new Uint32Array(cache), 
                bytes : () => {}, 
                bool : () => {}, 
                integer : () => {}, 
                float : () => {}, 
                custom : () => {}
            }
        };

        /**
         * @param {!string} data 
         * @param {!number} length 
         * @returns {void}
         * @access public
         */
        this.start = (data, length) => {
            self.action.start[self.name](data, length);
        };

        /**
         * @param {!number} value 
         * @param {!number} i 
         * @returns {void}
         * @access public
         */
        this.set_value = (value, i) => {
            self.action.set_value[self.name](value, i);
        }

        /**
         * @param {!number} empties 
         * @returns {void}
         * @access public
         */
        this.set_pre_empties = empties => {
            self.action.set_pre_empties[self.name](empties);
        }

        /**
         * @returns {void}
         * @access public
         */
        this.build = () => {
            self.action.build[self.name]();
        }

        /**
         * @returns {number}
         * @access public
         */
        this.length = () => (cache || self.data || []).length;

        constructor();
        
    };

    /** @type {Array.<string>} */
    AnyankaKeys.Type.TYPES = [
        "unknown", "string", "undefined", "null", "json", "utf8", "utf16", 
        "utf32", "bytes", "bool", "integer", "float", "custom"
    ];

    /**
     * @constructor
     * @param {?Object.<string, number>} [inputs = null] 
     * @returns {void}
     * @access public
     */
    AnyankaKeys.BaseChange = function(inputs = null){
        /** @type {number} */
        this.FROM = (inputs || (inputs = {})).from || inputs.base_from || AnyankaKeys.SETTINGS.base_from;
        /** @type {number} */
        this.TO = inputs.to || inputs.base_to || AnyankaKeys.SETTINGS.base_to;
    };

    /**
     * @constructor
     * @param {?Object.<string, number>} [inputs = null] 
     * @returns {void}
     * @access public
     */
    AnyankaKeys.BaseChange.Parameters = function(inputs){

        /** @type {AnyankaKeys.BaseChange.Parameters} */
        const self = this;
        /** @type {number} */
        let i = 0;

        /** @type {AnyankaKeys.BaseChange} */
        this.BASE = new AnyankaKeys.BaseChange(inputs);
        /** @type {number|null} */
        this.summatory = null;

        /**
         * @returns {void}
         * @access public
         */
        this.go_to_next = () => {
            self.summatory = (self.summatory || 0) / self.BASE.TO >> 0;
            i ++;
        };

        /**
         * @param {!number} value 
         * @returns {void}
         * @access public
         */
        this.add = value => {
            self.summatory = (self.summatory || 0) * self.BASE.FROM + value;
        };

        /**
         * @param {!anyanka_keys_convert_callback} callback 
         * @returns {void}
         * @access public
         */
        this.process = callback => {
            if(self.summatory !== null)
                while(self.summatory >= self.BASE.TO){
                    callback(self.summatory % self.BASE.TO, i);
                    self.go_to_next();
                };
        };

        /**
         * @param {!anyanka_keys_convert_callback} callback 
         * @returns {void}
         * @access public
         */
        this.end = callback => {
            self.process(callback);
            if(self.summatory !== null && (!i || self.summatory)){
                callback(self.summatory, i);
                self.go_to_next();
            };
        };

        /**
         * @param {!string} data 
         * @returns {number}
         * @access public
         */
        this.get_size = data => Math.round(data.length * Math.log2(self.BASE.FROM) / Math.log2(self.BASE.TO));

    };

    /**
     * @param {!(any|null)} value 
     * @returns {boolean}
     * @access public
     * @static
     */
    AnyankaKeys.is_dictionary = value => (
        value !== undefined && 
        value !== null && 
        value.constructor == Object
    );

    /**
     * @param {!string} string 
     * @param {!anyanka_keys_convert_callback} set 
     * @param {!AnyankaKeys.Options} options 
     * @returns {void}
     * @access public
     */
    AnyankaKeys.get_each_bytes_in_string = (string, set, options) => [...(
        options.scape_uri ? encodeURIComponent(string) : string
    )].forEach(character => {

        /** @type {number} */
        const code = character.charCodeAt(0), 
            /** @type {boolean} */
            is_utf8 = (code & 0xFF) == code;

        if(options.force_utf16)
            [8, 0].forEach(wise => set((code >> wise) & 0xFF));
        else if(options.scape){
            if(code == options.scape_character)
                [null, null].forEach(() => set(code));
            else if(is_utf8){
                set(code);
                options.scape_utf8 && set(options.scape_character);
            }else{
                [8, 0].forEach(wise => set((code >> wise) & 0xFF));
                options.scape_utf16 && set(options.scape_character);
            };
        }else 
            set(code);

    });

    /**
     * @param {!(any|null)} data 
     * @param {!anyanka_keys_convert_callback} set 
     * @param {!AnyankaKeys.Options} options 
     * @returns {AnyankaKeys.Type}
     * @access public
     * @static
     */
    AnyankaKeys.get_each_bytes = (data, set, options) => {

        /** @type {AnyankaKeys.Type} */
        let type = new AnyankaKeys.Type();

        if(data === undefined)
            type.set(2);
        else if(data === null)
            type.set(3);
        else 
            switch(typeof data){
                case "string":
                    AnyankaKeys.get_each_bytes_in_string(data, set, options);
                    type.set(1);
                break;
                case "boolean":
                    set(data ? 1 : 0);
                    type.set(9);
                break;
                case "number":
                    if(data == data >> 0){

                        let empties_done = false;

                        do{
                            set(data & 0xFF);
                            if(!empties_done){
                                if(data & 0xFF)
                                    empties_done = true;
                                else 
                                    type.empties ++;
                            };
                        }while(data >>= 8);

                        type.set(10);

                    }else{
                        [...("" + data)].forEach(character => set(character.charCodeAt(0)));
                        type.set(11);
                    };
                break;
                case "object":
                    if(data instanceof Uint8Array){
                        type.set(5);
                        [...data].forEach(set);
                    }else if(data instanceof Uint16Array){
                        [...data].forEach(smallint => [0, 1].forEach(i => set((smallint >> i * 8) & 0xFF)));
                        type.set(6);
                    }else if(data instanceof Uint32Array){
                        [...data].forEach(integer => [0, 1, 2, 3].forEach(i => set((integer >> i * 8) & 0xFF)));
                        type.set(7);
                    }else if(data instanceof Array && !data.some(value => typeof value != "number" || value < 0 || value > 0xFF)){
                        data.forEach(set);
                        type.set(8);
                    }else if(["Array", "Object"].includes(data.constructor.name)){
                        AnyankaKeys.get_each_bytes_in_string(JSON.stringify(data), set, options);
                        // [...JSON.stringify(data)].forEach(character => set(character.charCodeAt(0)));
                        type.set(4);
                    }else{
                        type.set(12, data.constructor.name);
                        try{
                            AnyankaKeys.get_each_bytes_in_string(JSON.stringify(data), set, options);
                            // [...JSON.stringify(data)].forEach(character => set(character.charCodeAt(0)));
                        }catch(exception){
                            type.set(0);
                        };
                    };
                break;
            };

        return type;
    };

    /**
     * @param {!(number|Array.<number>)} data 
     * @param {!anyanka_keys_convert_callback} set 
     * @param {?(AnyankaKeys.BaseChange.Parameters|Object.<string, any|null>)} [parameters = null] 
     * @returns {void}
     * @access public
     * @static
     */
    AnyankaKeys.change_base_iteration = (data, set, parameters = null) => {

        (
            !parameters || 
            !(parameters instanceof AnyankaKeys.BaseChange.Parameters)
        ) && (parameters = new AnyankaKeys.BaseChange.Parameters(inputs));

        (data instanceof Array ? data : [data]).forEach(value => {
            parameters.add(value);
            parameters.process(set);
        });

    };

    /**
     * @param {!(string|Array.<number>)} value 
     * @param {!number} to 
     * @param {?number} [from = null] 
     * @param {string|null} [type = null] 
     * @returns {string|Array.<number>|null}
     * @access public
     * @static
     */
    AnyankaKeys.change_base = (value, to, from = null, type = null) => {

        from || (from = AnyankaKeys.SETTINGS.base_from);
        type || (type = (
            typeof value == "number" && value == value >> 0 ? "integer" : 
            typeof value == "string" ? "string" : 
            value instanceof Array ? "array" : 
        "unknown"));

        /** @type {string|Array.<number>|null} */
        let response = null, 
            /** @type {anyanka_keys_convert_callback|null} */
            set = null, 
            /** @type {anyanka_keys_default_callback|null} */
            iterator = null;
        /** @type {AnyankaKeys.BaseChange.Parameters} */
        const parameters = new AnyankaKeys.BaseChange.Parameters({
            /** @type {number} */
            from : from, 
            /** @type {number} */
            to : to
        });

        switch(type){
            case "string":
                response = "";
                set = value => response = String.fromCharCode(value) + response;
                iterator = () => [...value].map(character => AnyankaKeys.change_base_iteration(character.charCodeAt(0), set, parameters));
            break;
            case "array":
                response = [];
                set = value => response.unshift(value);
                iterator = () => AnyankaKeys.change_base_iteration(value, set, parameters);
            break;
        };

        if(response !== null){
            iterator();
            parameters.end(set);
        };

        return response;
    };

    return AnyankaKeys;
})();