function f_1(x) {
    return x + (x * x * x - Math.log(x)) / Math.sqrt(x + 5);
}

function f_2(x) {
    return Math.pow(Math.sin(x), 2) - Math.abs(5 - Math.log10(x - 4));
}

function f_3(x) {
    return Math.exp(x - 2) + (x * x * x + 2 * x) / 4;
}

function memoize(fn) {
    const cache = new Map();
    const memoized = function (...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            console.log('Using cache');
            return cache.get(key);
        }
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };

    memoized.countMemoized = function () {
        return cache.size;
    };

    return memoized;
}

function debug(fn) {
    return function (...args) {
        const result = fn(...args);

        console.log(
            `Called ${
                fn.name
            } at ${new Date().toISOString()} with args ${args}. Result: ${result}`
        );

        return result;
    };
}

function countCalls(fn) {
    let count = 0;
    const wrapped = function (...args) {
        count++;
        return fn(...args);
    };

    wrapped.countCalls = function () {
        return count;
    };

    wrapped.resetCount = function () {
        count = 0;
    };

    return wrapped;
}

function findMin(fn, start, end, h) {
    let min = Infinity;
    for (let x = start; x < end; x += h) {
        const curr = fn(x);
        if (curr < min) {
            min = curr;
        }
    }

    return min;
}

function countPositive(fn, start, end, h) {
    let count = 0;
    for (let x = start; x < end; x += h) {
        if (fn(x) > 0) {
            count++;
        }
    }

    return count;
}

function isMonotonous(fn, start, end, h) {
    let prev = fn(start);
    for (let x = start; x < end; x += h) {
        const curr = fn(x);
        if (curr < prev) {
            return false;
        }
        prev = curr;
    }

    return true;
}

function calculateCharacteristics(fn, start, end, h, characteristics) {
    chars = [];
    for (let i = 0; i < characteristics.length; i++) {
        chars.push(characteristics[i](fn, start, end, h));
    }

    return chars;
}
