```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Find Most Ancestral Elements with Progress Feedback</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            position: relative; /* To position the target rectangle absolutely within the body */
        }

        /* Button Styles */
        .buttons {
            margin-bottom: 20px;
        }

        button {
            padding: 10px 20px;
            margin-right: 10px;
            font-size: 16px;
            cursor: pointer;
        }

        /* Progress Bar Styles */
        #progress-container {
            width: 100%;
            background-color: #f3f3f3;
            border: 1px solid #ccc;
            border-radius: 5px;
            overflow: hidden;
            margin-bottom: 10px;
            height: 25px;
        }

        #progress-bar {
            width: 0%;
            height: 100%;
            background-color: #4caf50;
            transition: width 0.2s ease;
        }

        #progress-text {
            display: block;
            text-align: right;
            margin-top: -25px;
            margin-bottom: 20px;
            font-weight: bold;
        }

        /* Elements Container */
        #elements-container {
            width: 800px; /* Define width */
            height: 600px; /* Define height */
            display: block; /* Change from flex to block for absolute positioning */
            max-height: 600px;
            overflow: auto;
            border: 1px solid #ccc;
            padding: 10px;
            position: relative; /* To position nested elements if needed */
        }

        /* Generated Elements */
        .generated-element {
            width: 50px;
            height: 50px;
            background-color: #add8e6;
            border: 1px solid #000;
            box-sizing: border-box;
        }

        /* Results */
        #results {
            margin-top: 20px;
        }

        #results ul {
            list-style-type: none;
            padding-left: 0;
        }

        #results li {
            padding: 5px 0;
        }

        /* Target Rectangle Styles */
        #target-rect {
            position: fixed;
            border: 2px solid red;
            pointer-events: none; /* Allows clicks to pass through the rectangle */
            z-index: 1000; /* Ensure it's on top of other elements */
            display: none; /* Hidden by default */
        }
    </style>
</head>
<body>

    <h1>Find Most Ancestral Elements with Progress Feedback</h1>

    <div class="buttons">
        <button id="generate-btn">Generate Elements</button>
        <button id="create-target-btn">Create Target Rect</button>
        <button id="search-btn" disabled>Start Search</button>
    </div>

    <div id="progress-container">
        <div id="progress-bar"></div>
    </div>
    <span id="progress-text">0%</span>

    <h2>Generated Elements</h2>
    <div id="elements-container"></div>

    <!-- Target Rectangle -->
    <div id="target-rect"></div>

    <h2>Search Results</h2>
    <div id="results">
        <ul id="results-list"></ul>
    </div>

    <script>
        // Optimized findMostAncestralElements function using closure for caching
        const findMostAncestralElements = (function () {
            // Cached helper functions
            function isMatch(rect, target, z) {
                const { left, top, width: rectWidth, height: rectHeight } = rect;
                const { x, y, width: targetWidth, height: targetHeight } = target;
                const { abs } = Math;

                return (
                    abs(left - x) <= targetWidth * z &&
                    abs(top - y) <= targetHeight * z &&
                    abs(rectWidth - targetWidth) <= targetWidth * z &&
                    abs(rectHeight - targetHeight) <= targetHeight * z
                );
            }

            function isOverlap(rect, target) {
                const { left, top, width: rectWidth, height: rectHeight } = rect;
                const { x, y, width: targetWidth, height: targetHeight } = target;

                return (
                    left < x + targetWidth &&
                    left + rectWidth > x &&
                    top < y + targetHeight &&
                    top + rectHeight > y
                );
            }

            function createProcessBatchAsync(queue, target, z, exhaustive, iterationBatch, onProgress, head, results) {
                return function processBatchAsync() {
                    return new Promise((resolve) => {
                        let processed = 0;

                        while (head.value < queue.length && processed < iterationBatch) {
                            const elem = queue[head.value++];
                            const rect = elem.getBoundingClientRect();

                            if (isMatch(rect, target, z)) {
                                results.add(elem);
                                // ==== for debugging ====
                                console.log(elem);
                                // debugger; // Commented out to prevent interruption
                                const res  = isMatch(rect, target, z);
                                // ==== for debugging ====
                                // Skip enqueuing children to maintain ancestry
                            } else if (exhaustive || isOverlap(rect, target)) {
                                const children = elem.children;
                                for (let i = 0, len = children.length; i < len; i++) {
                                    queue.push(children[i]);
                                }
                            }

                            processed++;
                        }

                        if (onProgress) {
                            onProgress({
                                processedElements: head.value,
                                totalQueued: queue.length,
                                uniqueResults: results.size,
                            });
                        }

                        resolve();
                    });
                };
            }

            /**
             * The optimized findMostAncestralElements function.
             *
             * @param {number} x - The X-coordinate of the target rectangle (relative to the viewport).
             * @param {number} y - The Y-coordinate of the target rectangle (relative to the viewport).
             * @param {number} width - The width of the target rectangle.
             * @param {number} height - The height of the target rectangle.
             * @param {number} errorRatio - The allowed error ratio (0 to 1) for matching.
             * @param {Object} [options] - Optional settings.
             * @param {boolean} [options.exhaustive=true] - If true, performs an exhaustive search.
             * @param {number} [options.asyncIterations] - Number of iterations per asynchronous batch.
             *                                             If null, undefined, or <= 0, executes synchronously.
             * @param {Function} [options.onProgress] - Callback function to receive progress updates.
             * @returns {Promise<Element[]>|Element[]} A promise that resolves to an array of the most ancestral DOM elements matching the criteria, or the array directly if not asynchronous.
             */
            return function findMostAncestralElements(
                x,
                y,
                width,
                height,
                errorRatio,
                options = {}
            ) {
                const {
                    exhaustive = true,
                    asyncIterations, // Renamed from 'async' to 'asyncIterations'
                    onProgress,
                } = options;

                // Determine if the function should execute asynchronously
                const isAsync = typeof asyncIterations === 'number' && asyncIterations > 0;
                const iterationBatch = isAsync ? asyncIterations : null;

                // Clamp errorRatio between 0 and 1
                const z = Math.max(0, Math.min(errorRatio, 1));

                const target = { x, y, width, height };
                const results = new Set();
                const queue = [document.body];
                const head = { value: 0 }; // Using an object to allow mutation inside closure

                if (isAsync) {
                    const processBatch = createProcessBatchAsync(
                        queue,
                        target,
                        z,
                        exhaustive,
                        iterationBatch,
                        onProgress,
                        head,
                        results
                    );

                    return new Promise((resolve) => {
                        function iterate() {
                            processBatch().then(() => {
                                if (head.value < queue.length) {
                                    setTimeout(iterate, 0);
                                } else {
                                    resolve(Array.from(results));
                                }
                            });
                        }
                        iterate();
                    });
                } else {
                    // Synchronous Processing
                    while (head.value < queue.length) {
                        const elem = queue[head.value++];
                        const rect = elem.getBoundingClientRect();

                        if (isMatch(rect, target, z)) {
                            results.add(elem);
                            continue; // Skip enqueuing children to maintain ancestry
                        }

                        if (exhaustive || isOverlap(rect, target)) {
                            const children = elem.children;
                            for (let i = 0, len = children.length; i < len; i++) {
                                queue.push(children[i]);
                            }
                        }

                        if (onProgress) {
                            onProgress({
                                processedElements: head.value,
                                totalQueued: queue.length,
                                uniqueResults: results.size,
                            });
                        }
                    }
                    return Array.from(results);
                }
            };
        })();

        // Function to update the progress bar and percentage text
        function updateProgressBar(progress) {
            const { processedElements, totalQueued } = progress;
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');

            // Estimate percentage based on processed vs total queued
            const percentage = totalQueued > 0 ? Math.min((processedElements / (processedElements + totalQueued)) * 100, 100) : 100;

            progressBar.style.width = `${percentage}%`;
            progressText.textContent = `${percentage.toFixed(2)}%`;
        }

        // Corrected Function to Generate Elements in a Grid Without Overlapping
        function generateElements(count = 50, maxDepth = 2) {
            const container = document.getElementById('elements-container');
            container.innerHTML = ''; // Clear previous elements if any

            const ELEMENT_SIZE = 50; // px
            const GAP = 10; // px

            // Get container dimensions
            const containerRect = container.getBoundingClientRect();
            const containerWidth = containerRect.width - GAP; // Adjust for padding and gaps
            const containerHeight = containerRect.height - GAP;

            const columns = Math.floor((containerWidth) / (ELEMENT_SIZE + GAP));
            const rows = Math.floor((containerHeight) / (ELEMENT_SIZE + GAP));

            let currentColumn = 0;
            let currentRow = 0;

            // Function to get position based on row and column
            function getPosition(row, column) {
                const left = column * (ELEMENT_SIZE + GAP);
                const top = row * (ELEMENT_SIZE + GAP);
                return { left, top };
            }

            // Function to create nested elements with controlled branching
            function createNestedElements(parent, currentDepth) {
                if (currentDepth > maxDepth) return;

                // Define a fixed number of child elements per parent to prevent exponential growth
                const CHILDREN_PER_PARENT = 2; // Fixed branching factor

                for (let i = 0; i < CHILDREN_PER_PARENT; i++) {
                    const div = document.createElement('div');
                    div.classList.add('generated-element');

                    // Position children within the parent with a smaller gap
                    const childGap = 5; // px
                    const childLeft = (i % 2) * (ELEMENT_SIZE + childGap);
                    const childTop = Math.floor(i / 2) * (ELEMENT_SIZE + childGap);

                    // Apply inline styles for absolute positioning
                    div.style.position = 'absolute';
                    div.style.left = `${childLeft}px`;
                    div.style.top = `${childTop}px`;

                    parent.appendChild(div);

                    // Randomly decide to nest further with controlled depth
                    if (Math.random() > 0.5) { // 50% chance to nest for demonstration
                        createNestedElements(div, currentDepth + 1);
                    }
                }
            }

            // Generate top-level elements in grid
            for (let i = 0; i < count; i++) {
                if (currentRow >= rows) {
                    console.warn('Reached maximum rows. Some elements may not be visible.');
                    break;
                }

                const div = document.createElement('div');
                div.classList.add('generated-element');

                const row = currentRow;
                const column = currentColumn;
                const { left, top } = getPosition(row, column);

                // Apply inline styles for absolute positioning
                div.style.position = 'absolute';
                div.style.left = `${left}px`;
                div.style.top = `${top}px`;

                container.appendChild(div);

                // Randomly decide to nest further with controlled branching
                if (Math.random() > 0.7) { // 30% chance to nest
                    createNestedElements(div, 1);
                }

                // Update row and column
                currentColumn++;
                if (currentColumn >= columns) {
                    currentColumn = 0;
                    currentRow++;
                }
            }
        }

        // Function to display search results
        function displayResults(elements) {
            const resultsList = document.getElementById('results-list');
            resultsList.innerHTML = ''; // Clear previous results

            if (elements.length === 0) {
                resultsList.innerHTML = '<li>No matching elements found.</li>';
                return;
            }

            elements.forEach((elem, index) => {
                const li = document.createElement('li');
                li.textContent = `Element ${index + 1}: ${elem.tagName}${elem.id ? `#${elem.id}` : ''}${elem.className ? `.${elem.className}` : ''}`;
                resultsList.appendChild(li);
                console.log(elem);
            });
            window.results = Array.from(elements);
        }

        // Function to draw or update the target rectangle
        function drawTargetRect(targetRect) {
            const targetDiv = document.getElementById('target-rect');
            // Set the position and size based on targetRect
            targetDiv.style.left = `${targetRect.x}px`;
            targetDiv.style.top = `${targetRect.y}px`;
            targetDiv.style.width = `${targetRect.width}px`;
            targetDiv.style.height = `${targetRect.height}px`;
            // Display the rectangle
            targetDiv.style.display = 'block';
        }

        // Function to create a random target rectangle
        function createRandomTargetRect() {
            const targetDiv = document.getElementById('target-rect');

            // Define target rectangle dimensions
            const width = 100; // px
            const height = 100; // px

            // Calculate random positions within the container
            const container = document.getElementById('elements-container');
            const containerRect = container.getBoundingClientRect();

            const maxX = containerRect.width - width - 10; // Adjust for padding and element size
            const maxY = containerRect.height - height - 10;

            const x = Math.random() * maxX;
            const y = Math.random() * maxY;

            const targetRect = { x, y, width, height };

            drawTargetRect(targetRect);
            return targetRect;
        }

        // Event listeners for buttons
        document.getElementById('generate-btn').addEventListener('click', () => {
            const generateBtn = document.getElementById('generate-btn');
            const createTargetBtn = document.getElementById('create-target-btn');
            const searchBtn = document.getElementById('search-btn');
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');
            const resultsList = document.getElementById('results-list');

            // Disable buttons during generation
            generateBtn.disabled = true;
            createTargetBtn.disabled = true;
            searchBtn.disabled = true;
            resultsList.innerHTML = '';
            progressBar.style.width = '0%';
            progressText.textContent = '0%';

            // Hide the target rectangle when generating new elements
            const targetDiv = document.getElementById('target-rect');
            targetDiv.style.display = 'none';

            // Generate elements
            setTimeout(() => { // Use setTimeout to allow UI to update
                generateElements(100, 2); // Generate 50 elements with max depth 2
                generateBtn.disabled = false;
                createTargetBtn.disabled = false;
                searchBtn.disabled = false;
                alert('Element generation complete!');
            }, 100); // Slight delay to ensure buttons get disabled first
        });

        document.getElementById('create-target-btn').addEventListener('click', () => {
            const targetRect = createRandomTargetRect();
            console.log('Target Rectangle Created:', targetRect);
            alert('Target Rectangle Created!');
        });

        document.getElementById('search-btn').addEventListener('click', () => {
            const searchBtn = document.getElementById('search-btn');
            const resultsList = document.getElementById('results-list');

            // Clear previous results
            resultsList.innerHTML = '';
            // Reset progress bar
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');
            progressBar.style.width = '0%';
            progressText.textContent = '0%';

            // Disable buttons during search
            const generateBtn = document.getElementById('generate-btn');
            const createTargetBtn = document.getElementById('create-target-btn');
            generateBtn.disabled = true;
            createTargetBtn.disabled = true;
            searchBtn.disabled = true;

            let targetRect = getCurrentTargetRect();

            // If no target rect exists, create one
            if (!targetRect) {
                targetRect = createRandomTargetRect();
                alert('No Target Rectangle found. Creating one for you.');
            }

            // Log the target rectangle
            console.log('Using Target Rectangle:', targetRect);

            // Start search
            findMostAncestralElements(
                targetRect.x,
                targetRect.y,
                targetRect.width,
                targetRect.height,
                0.5, // errorRatio
                {
                    asyncIterations: 100, // Process 100 elements per batch
                    onProgress: (progress) => {
                        updateProgressBar(progress);
                    }
                }
            ).then((matchingElements) => {
                displayResults(matchingElements);
                alert(`Search complete! Found ${matchingElements.length} matching elements.`);
                generateBtn.disabled = false;
                createTargetBtn.disabled = false;
                searchBtn.disabled = false;
            }).catch((error) => {
                console.error('Error during search:', error);
                alert('An error occurred during the search.');
                generateBtn.disabled = false;
                createTargetBtn.disabled = false;
                searchBtn.disabled = false;
            });
        });

        // Helper function to get the current target rectangle
        function getCurrentTargetRect() {
            const targetDiv = document.getElementById('target-rect');
            if (targetDiv.style.display === 'none') return null;

            const left = parseFloat(targetDiv.style.left);
            const top = parseFloat(targetDiv.style.top);
            const width = parseFloat(targetDiv.style.width);
            const height = parseFloat(targetDiv.style.height);

            if (isNaN(left) || isNaN(top) || isNaN(width) || isNaN(height)) {
                return null;
            }

            return { x: left, y: top, width, height };
        }
    </script>

</body>
</html>
```
add input text boxes to allow user to specify the parameters for generateElements.
the boxes should be pre-filled with their default value
if errorneous input, report it in ui. (not alert)

also, allow user to define its own rect with input boxes too. (with an update rect button)
if errorneous input, report it in ui. (not alert)

the create rect button remains effective for randomized rect.