// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Median of Two Sorted Arrays Example
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef MEDIAN_FINDER_H
#define MEDIAN_FINDER_H

#include <vector>
#include <string>

/**
 * MedianFinder class provides efficient algorithms to find the median
 * of two sorted arrays using different approaches.
 * 
 * This class implements both binary search (O(log(min(m,n)))) and 
 * merge-based (O(m+n)) approaches for educational purposes.
 */
class MedianFinder {
public:
    // LIFECYCLE
    MedianFinder() = delete;  // Utility class - prevent instantiation
    ~MedianFinder() = delete;
    
    // OPERATORS
    MedianFinder(const MedianFinder&) = delete;
    MedianFinder& operator=(const MedianFinder&) = delete;
    
    // OPERATIONS
    /**
     * Finds the median of two sorted arrays using binary search.
     * This is the optimal solution with logarithmic time complexity.
     * 
     * @param nums1 First sorted array (const reference)
     * @param nums2 Second sorted array (const reference)
     * @return The median value as double
     * @throws std::invalid_argument if arrays are not properly sorted
     * 
     * Time Complexity: O(log(min(m,n))) where m and n are array sizes
     * Space Complexity: O(1)
     */
    static double FindMedianSortedArrays(const std::vector<int>& nums1, 
                                       const std::vector<int>& nums2);
    
    /**
     * Alternative approach using merge technique.
     * Less efficient but easier to understand and implement.
     * 
     * @param nums1 First sorted array (const reference)
     * @param nums2 Second sorted array (const reference)
     * @return The median value as double
     * 
     * Time Complexity: O(m + n)
     * Space Complexity: O(1)
     */
    static double FindMedianMergeApproach(const std::vector<int>& nums1,
                                        const std::vector<int>& nums2);
    
    /**
     * Validates that an array is sorted in non-decreasing order.
     * 
     * @param arr Array to validate
     * @return true if array is sorted, false otherwise
     */
    static bool IsArraySorted(const std::vector<int>& arr);
    
    /**
     * Gets algorithm complexity information as string.
     * 
     * @param use_binary_search If true, returns binary search complexity info
     * @return String describing time and space complexity
     */
    static std::string GetComplexityInfo(bool use_binary_search = true);

private:
    // CONSTANTS
    static const double EPSILON;  // For floating point comparisons
    
    // HELPER METHODS
    /**
     * Helper method to find the correct partition point in binary search.
     * 
     * @param nums1 Smaller array
     * @param nums2 Larger array
     * @param left Left boundary for binary search
     * @param right Right boundary for binary search
     * @param half_length Half of total combined length
     * @return Partition point or -1 if not found
     */
    static int FindPartition(const std::vector<int>& nums1,
                           const std::vector<int>& nums2,
                           int left, int right, int half_length);
};

/**
 * MedianTester class provides comprehensive testing functionality
 * for the MedianFinder algorithms.
 */
class MedianTester {
public:
    // LIFECYCLE
    MedianTester() = default;
    ~MedianTester() = default;
    
    // OPERATIONS
    /**
     * Runs all predefined test cases for median finding algorithms.
     * Compares results from both binary search and merge approaches.
     */
    static void RunTests();
    
    /**
     * Runs a single test case with detailed output.
     * 
     * @param nums1 First test array
     * @param nums2 Second test array
     * @param expected Expected median result
     * @param description Test case description
     * @return true if test passes, false otherwise
     */
    static bool RunSingleTest(const std::vector<int>& nums1,
                            const std::vector<int>& nums2,
                            double expected,
                            const std::string& description);
    
    /**
     * Generates random test cases for stress testing.
     * 
     * @param num_tests Number of random tests to generate
     * @param max_size Maximum size for generated arrays
     * @param max_value Maximum absolute value for array elements
     */
    static void RunRandomTests(int num_tests = 100, 
                             int max_size = 1000, 
                             int max_value = 10000);

private:
    // HELPER METHODS
    /**
     * Prints an array in a formatted way.
     * 
     * @param arr Array to print
     * @param max_elements Maximum number of elements to display
     */
    static void PrintArray(const std::vector<int>& arr, int max_elements = 10);
    
    /**
     * Generates a random sorted array.
     * 
     * @param size Size of array to generate
     * @param max_value Maximum absolute value for elements
     * @return Generated sorted array
     */
    static std::vector<int> GenerateRandomSortedArray(int size, int max_value);
    
    /**
     * Calculates expected median using brute force approach.
     * Used for validation in random testing.
     * 
     * @param nums1 First array
     * @param nums2 Second array
     * @return Expected median value
     */
    static double CalculateExpectedMedian(const std::vector<int>& nums1,
                                        const std::vector<int>& nums2);
    
    // ATTRIBUTES
    static int mTestCount;      // Total number of tests run
    static int mPassedTests;    // Number of tests that passed
    static int mFailedTests;    // Number of tests that failed
};

#endif // MEDIAN_FINDER_H