diff --git a/algorithms-java/src/main/java/com/sl/algorithms/sort/generalpurpose/smalldata/SelectionSort.java b/algorithms-java/src/main/java/com/sl/algorithms/sort/generalpurpose/smalldata/SelectionSort.java index 318565e3..52503565 100644 --- a/algorithms-java/src/main/java/com/sl/algorithms/sort/generalpurpose/smalldata/SelectionSort.java +++ b/algorithms-java/src/main/java/com/sl/algorithms/sort/generalpurpose/smalldata/SelectionSort.java @@ -6,11 +6,10 @@ import com.sl.algorithms.core.list.ListNode; /** - *
In-place quadratic-complexity sort algorithm, useful for small data-set.

Reference
- *
Approach: Given a list, take the current element and exchange it with the - * smallest element on the right.

Usage: Small data-set / when 'write' operation - * is expensive.

Inner Loop: operates on the Unsorted portion.
+ *

In-place quadratic-complexity algorithm, useful for small data-set.

+ *

Given a list, take the current element and exchange it with the smallest element.

+ *

Usage: Small data-set / when 'write' operation is expensive.

+ *

Inner Loop: operates on the Unsorted portion.

*/ @SuppressWarnings("unchecked") public class SelectionSort implements SortingEngine { diff --git a/algorithms-js/README.md b/algorithms-js/README.md index 26ee6131..3ce3b946 100644 --- a/algorithms-js/README.md +++ b/algorithms-js/README.md @@ -18,7 +18,18 @@ ### 🚀 Quick Start -#### Option 0: Root-level dev helper +#### Option 0: algorithms-js dev helper (Recommended) + +```bash +# From algorithms-js directory +cd algorithms-js +./dev.sh start # start http://localhost:8080 +./dev.sh status # check status +./dev.sh stop # stop server +./dev.sh restart # restart server +``` + +#### Option 1: Root-level dev helper ```bash # From repo root @@ -27,7 +38,7 @@ ./dev.sh stop # stop server ``` -#### Option 1: Using root build script (Recommended) +#### Option 2: Using root build script ```bash # Clone the repository @@ -42,7 +53,7 @@ cd algorithms-js npm start ``` -#### Option 2: Using npm scripts directly +#### Option 3: Using npm scripts directly ```bash # Clone the repository @@ -56,7 +67,7 @@ npm start npm run dev ``` -#### Option 3: Manual server setup +#### Option 4: Manual server setup ```bash # Using Python (recommended - no redirect issues) @@ -70,7 +81,7 @@ npx live-server --port=8080 --no-browser Then open: **http://localhost:8080** -#### Option 4: Using the helper script (with dependency checks) +#### Option 5: Using the helper script (with dependency checks) ```bash # From repo root diff --git a/algorithms-js/TEMPLATE-SYSTEM.md b/algorithms-js/TEMPLATE-SYSTEM.md deleted file mode 100644 index 5959171f..00000000 --- a/algorithms-js/TEMPLATE-SYSTEM.md +++ /dev/null @@ -1,303 +0,0 @@ -# Algorithm Template System - -This document explains the template system used to generate uniform HTML pages for algorithm demonstrations in SimplifyLearning. - -## Overview - -The template system eliminates code duplication across algorithm pages by generating HTML from configuration files. This ensures: -- **Consistency**: All algorithm pages have the same structure and styling -- **Maintainability**: Changes to the template affect all pages -- **Simplicity**: Adding new algorithms requires only a configuration file - -## System Components - -### 1. Template Generator (`assets/js/algorithm-template-generator.js`) -The main class that generates HTML from configuration objects. Features: -- Validates configuration properties -- Generates uniform HTML structure -- Supports customization through config options -- Handles different input types and visualization needs - -### 2. Build Script (`build-pages.js`) -Node.js script that: -- Scans for `config.js` files in the `src/` directory -- Generates HTML files from configurations -- Provides watch mode for development -- Reports build status and errors - -### 3. Configuration Files (`src/*/config.js`) -JSON-like objects that define algorithm page structure: -- Algorithm metadata (name, category, GitHub link) -- Input field definitions -- Explanation content -- Custom demo functions -- Styling overrides - -## Quick Start - -### Running the Application -```bash -# Serve the application (no build step required) -npm start - -# Or for development -npm run dev - -# Or manually serve -npm run serve -``` - -**Note:** With the dynamic template system, no build step is required. Pages are generated at runtime. - -### Adding a New Algorithm - -1. **Create the algorithm directory structure:** - ``` - src/category/algorithm-name/ - ├── algorithm-name.js # Algorithm implementation - ├── algorithm-name.html # Generated (don't edit manually) - └── config.js # Configuration file - ``` - -2. **Create a configuration file** (`config.js`): - ```javascript - const config = { - name: "Algorithm Name", - title: "Algorithm Name Demo", - category: "category", - cssPath: "../../../assets/css/styles.css", - jsPath: "algorithm-name.js", - githubPath: "https://github.com/sachinlala/SimplifyLearning/blob/master/algorithms-js/src/category/algorithm-name/algorithm-name.js", - algorithmFunction: "mainAlgorithmFunction", - - inputs: [ - { - id: "input-id", - type: "number", // or "text", "range" - label: "Input Label", - defaultValue: 10, - min: 1, - max: 100, - width: "80px" - } - ], - - example: "Optional example text", - - explanation: { - description: "How the algorithm works...", - steps: [ - "Step 1 explanation", - "Step 2 explanation" - ] - } - }; - - module.exports = config; - ``` - -3. **Run the build script:** - ```bash - npm run build - ``` - -The HTML file will be automatically generated in the same directory. - -## Configuration Options - -### Required Properties -- `name`: Display name of the algorithm -- `title`: HTML page title -- `category`: Algorithm category (patterns, search, sort, etc.) -- `cssPath`: Relative path to CSS file -- `jsPath`: Algorithm JavaScript file name -- `githubPath`: GitHub source code URL -- `inputs`: Array of input field configurations -- `explanation`: Object with description and steps - -### Optional Properties -- `algorithmFunction`: Main function name (default: "runAlgorithm") -- `hasVisualization`: Enable visualization section (default: false) -- `autoRun`: Auto-run demo on load (default: true) -- `example`: Example text to show in input section -- `customDemoFunction`: Override default demo function -- `customStyles`: Additional CSS styles -- `backPath`: Custom back navigation path - -### Input Field Types - -#### Number Input -```javascript -{ - id: "number-input", - type: "number", - label: "Number Field", - min: 1, - max: 100, - defaultValue: 10, - width: "80px" -} -``` - -#### Text Input -```javascript -{ - id: "text-input", - type: "text", - label: "Text Field", - defaultValue: "1,2,3,4,5", - width: "200px" -} -``` - -#### Range Input -```javascript -{ - id: "range-input", - type: "range", - label: "Range Field", - min: 1, - max: 100, - defaultValue: 50, - width: "120px" -} -``` - -## Custom Demo Functions - -For complex algorithms, you can provide a custom demo function: - -```javascript -const config = { - // ... other properties - customDemoFunction: ` - function runDemo() { - // Get input values - const input1 = document.getElementById('input-1').value; - const input2 = parseInt(document.getElementById('input-2').value); - - // Clear previous results - const resultContainer = document.getElementById('result'); - const errorContainer = document.getElementById('error-message'); - errorContainer.style.display = 'none'; - - try { - // Call your algorithm function - const result = myAlgorithmFunction(input1, input2); - - // Display result - resultContainer.innerHTML = \`Result: \${result}\`; - - } catch (error) { - showError(error.message); - } - }` -}; -``` - -## Visualization Support - -For algorithms that need step-by-step visualization, set `hasVisualization: true`: - -```javascript -const config = { - hasVisualization: true, - customDemoFunction: ` - function runDemo() { - // ... algorithm logic - - // Show visualization - const visualizationSection = document.getElementById('visualization-section'); - visualizationSection.style.display = 'block'; - - // Use these containers: - // - document.getElementById('array-visualization') - // - document.getElementById('steps-container') - }` -}; -``` - -## File Structure - -``` -algorithms-js/ -├── assets/ -│ └── js/ -│ ├── algorithm-template-generator.js # Template generator -│ ├── components.js # UI components -│ └── ui.js # Main UI logic -├── src/ -│ ├── patterns/ -│ │ └── count-and-say/ -│ │ ├── config.js # Configuration -│ │ ├── count-and-say.js # Implementation -│ │ └── count-and-say.html # Generated HTML -│ ├── search/ -│ └── sort/ -├── build-pages.js # Build script -├── package.json # NPM configuration -└── TEMPLATE-SYSTEM.md # This documentation -``` - -## Best Practices - -1. **Keep configurations simple**: Use custom demo functions only when necessary -2. **Follow naming conventions**: Use kebab-case for IDs and file names -3. **Test generated pages**: Always test the generated HTML after building -4. **Use appropriate input types**: Choose the right input type for better UX -5. **Provide good examples**: Include helpful example text for complex inputs -6. **Write clear explanations**: Make algorithm descriptions accessible to learners - -## Generated Files - -HTML files are generated with a `generated-` prefix (e.g., `generated-count-and-say.html`) and are **automatically ignored by git**. This ensures: - -- Only source files (configs) are tracked in version control -- No risk of HTML/config getting out of sync -- Cleaner git history and smaller repository size -- Forces proper build process usage - -## Deployment - -The project uses GitHub Actions for automated deployment: - -1. **On push to main/master**: GitHub Actions automatically builds HTML pages and deploys to GitHub Pages -2. **Local development**: Run `npm run build` to generate pages for testing -3. **Pull requests**: Pages are built but not deployed (for validation) - -### GitHub Pages Setup - -The repository includes a GitHub Actions workflow (`.github/workflows/deploy-pages.yml`) that: -- Installs Node.js dependencies -- Runs the build process to generate HTML files -- Deploys the `algorithms-js` folder to GitHub Pages - -To enable GitHub Pages deployment: -1. Go to repository Settings → Pages -2. Set Source to "GitHub Actions" -3. The workflow will run automatically on commits to main branch - -## Development Workflow - -1. **Development mode**: Run `npm run dev` to watch for config changes -2. **Make changes**: Edit configuration files or algorithm implementations -3. **Test locally**: Use `npm start` to build and serve the application -4. **Review generated HTML**: Ensure the output matches expectations -5. **Commit changes**: Only commit config files and source code (generated HTML is ignored) - -## Troubleshooting - -### Build Errors -- Check configuration syntax for missing commas or brackets -- Ensure all required properties are present -- Verify file paths are correct relative to the HTML location - -### Runtime Errors -- Make sure algorithm function names match the configuration -- Check that all required DOM elements have correct IDs -- Verify JavaScript implementations are compatible with the template - -### Styling Issues -- Use `customStyles` property for algorithm-specific CSS -- Ensure dark mode compatibility in custom styles -- Test both light and dark themes diff --git a/algorithms-js/UI_UX.md b/algorithms-js/UI_UX.md index 0ab7a78f..a649ea7c 100644 --- a/algorithms-js/UI_UX.md +++ b/algorithms-js/UI_UX.md @@ -1,6 +1,6 @@ # UI/UX - Implementation Summary -## 🎨 Design System +## 🌨 Design System ### Color Palette: - **Light Mode Header/Footer**: Linear gradient from #e3f2fd to #bbdefb @@ -8,39 +8,101 @@ - **Primary Blue**: #2196f3 for count badges and buttons - **Category Tags**: Blue gradient with green selection state - **Links**: #007acc with proper hover states +- **Visualization Colors**: Consistent color scheme across all algorithm demos + - 🔵 Unsorted/Default elements + - 🟡 Comparing/Current elements + - 🟢 Sorted/Complete elements + - 🔴 Error/Highlight states ### Typography: - **Font Family**: Roboto (300, 400, 500, 700 weights) - **Headers**: Reduced sizes for more compact design - **Content**: Improved line heights and spacing +- **Code/Monospace**: Consistent styling for algorithm step descriptions + +## 🎨 Current Architecture (Phase 2) + +### Universal Template System: +- **Dynamic Templates**: Single `dynamic-template.js` generates all algorithm pages +- **Universal Loader**: `universal-loader.js` handles config loading and page generation +- **Config-Driven**: Each algorithm has a `.config.js` with metadata and demo functions +- **Step Visualization**: Optional step-by-step animations via `hasStepsFile` flag + +### File Structure: +``` +src/ +├── category/ +│ └── algorithm-name/ +│ ├── algorithm-name.config.js # Demo configuration +│ ├── algorithm-name-core.js # Core implementation +│ ├── algorithm-name-steps.js # Step tracking (optional) +│ └── algorithm-name.js # Demo functions +``` + +### Key Features: +- **Zero 404 Errors**: Smart optional script loading +- **Mobile-First**: Responsive legends and controls +- **Theme Support**: Unified dark/light mode across all pages +- **Clean Console**: Informative logging without clutter ## 🚀 Usage Instructions ### Development Server: ```bash -# Recommended (no redirect issues) -npm start # Uses Python server on port 8080 +# Primary development server (recommended) +./dev.sh start # Starts Python server on port 8080 +./dev.sh stop # Stops the server +./dev.sh restart # Restarts the server +./dev.sh status # Check server status -# Alternative servers -npm run serve:node # Node.js with --no-clean-urls -npm run serve:http # http-server with extension redirects disabled -npm run serve:live # live-server with auto-reload +# Legacy npm scripts (still available) +npm start # Alternative Python server start +npm run serve:* # Various server options ``` +### Universal Demo System: +- **Main Index**: `http://localhost:8080/index.html` - Algorithm catalog +- **Algorithm Demos**: `http://localhost:8080/demo.html?algo=category/algorithm` + - Example: `demo.html?algo=search/binary-search` + - Example: `demo.html?algo=sort/bubble-sort` +- **Universal Loader**: Dynamically generates algorithm pages from configs + ### Testing: -1. Visit `http://localhost:8080` (or appropriate port) -2. Test hamburger menu navigation -3. Try theme toggle functionality -4. Test algorithm demos, especially Bubble Sort visualization -5. Verify mobile responsiveness +1. Visit `http://localhost:8080` for main algorithm catalog +2. Test individual algorithm demos via `demo.html?algo=...` URLs +3. Verify hamburger menu navigation and theme toggle +4. Test step-by-step visualizations (algorithms with `hasStepsFile: true`) +5. Check console for clean output (no 404 errors) +6. Verify mobile responsiveness across different screen sizes + +## 📋 Current Algorithm Catalog + +### Sorting Algorithms (11): +- **Comparison-Based**: Bubble Sort, Insertion Sort, Selection Sort, Merge Sort, Quick Sort, Heap Sort +- **Distribution-Based**: Counting Sort, Radix Sort, Bucket Sort +- **Specialized**: Dutch National Flag Sort, Wiggle Sort + +### Search Algorithms (1): +- **Binary Search**: Iterative and recursive implementations with visualization + +### Pattern Algorithms (1): +- **Count and Say**: Sequence generation with step-by-step visualization + +### Algorithm Features: +- ✅ **Step-by-Step Visualization**: All algorithms support animated step tracking +- ✅ **Multi-Input Support**: Numeric and alphanumeric data types +- ✅ **Performance Metrics**: Comparisons, swaps, execution time tracking +- ✅ **Mobile Responsive**: Adaptive legends and controls +- ✅ **Source Code Links**: JavaScript and Java implementations on GitHub ## 🔄 Future Enhancements -- Add Python and Go implementations -- Expand algorithm library with more categories -- Add search functionality within sidebar -- Implement algorithm difficulty levels -- Add algorithm performance metrics -- Create user favorites system +- Add Python and Go source code implementations +- Expand to graph, tree, and dynamic programming algorithms +- Add search functionality within algorithm catalog +- Implement algorithm difficulty levels and categories +- Add algorithm complexity analysis comparisons +- Create user favorites and recently viewed systems +- Add algorithm recommendation engine ## 📱 Browser Compatibility - ✅ Chrome/Edge (latest) @@ -49,10 +111,28 @@ npm run serve:live # live-server with auto-reload - ✅ Mobile browsers (iOS Safari, Chrome Mobile) ## 🎯 Accessibility Features -- Keyboard navigation support (Escape key for sidebar) -- Proper ARIA labels and semantic HTML -- Color contrast compliance -- Touch-friendly interface elements -- Screen reader friendly structure +- **Keyboard Navigation**: Escape key for sidebar, tab navigation support +- **ARIA Compliance**: Proper labels, roles, and semantic HTML structure +- **Color Contrast**: WCAG compliant color schemes in both light/dark modes +- **Touch-Friendly**: 44px minimum touch targets, responsive controls +- **Screen Reader**: Descriptive text, logical heading hierarchy +- **Visual Indicators**: Clear focus states and interactive element feedback +- **Responsive Legends**: Mobile-friendly visualization guides + +## 🛠️ Troubleshooting + +### Common Issues: +1. **404 Errors in Console**: Should be eliminated with current system +2. **Algorithm Not Loading**: Check `demo.html?algo=category/algorithm-name` format +3. **Visualization Not Working**: Ensure algorithm has `hasStepsFile: true` in config +4. **Server Won't Start**: Use `./dev.sh stop` then `./dev.sh start` +5. **Mobile Display Issues**: Test with responsive dev tools, check legend display + +### Debug Steps: +1. Check browser console for error messages +2. Verify algorithm config file exists and is properly formatted +3. Confirm step files exist if `hasStepsFile: true` is set +4. Test with different browsers to isolate compatibility issues +5. Check server logs with `./dev.sh status` --- diff --git a/algorithms-js/assets/js/algorithm-template-generator.js b/algorithms-js/assets/js/algorithm-template-generator.js deleted file mode 100644 index 27d3db0d..00000000 --- a/algorithms-js/assets/js/algorithm-template-generator.js +++ /dev/null @@ -1,608 +0,0 @@ -/** - * Algorithm Template Generator - * - * This module generates uniform HTML pages for algorithm demos based on configuration objects. - * It eliminates code duplication and ensures consistent structure across all algorithm pages. - */ - -class AlgorithmTemplateGenerator { - constructor() { - this.baseTemplate = null; - } - - /** - * Generate HTML page for an algorithm based on configuration - * @param {Object} config - Algorithm configuration object - * @returns {string} Complete HTML page content - */ - generateHTML(config) { - // Validate required config properties - this.validateConfig(config); - - const html = ` - - - - - ${config.title} - SimplifyLearning - - - - - - - ${this.generateHeader(config)} - -
- ${this.generateHeroSection(config)} - ${this.generateProblemSection(config)} - -
- ${this.generateThemeToggle()} - ${this.generateInputSection(config)} - ${this.generateOutputSection(config)} - ${config.hasVisualization ? this.generateVisualizationSection(config) : ''} - ${this.generateExplanationSection(config)} - ${this.generateSourceCodeSection(config)} -
-
- - ${this.generateFooter()} - - ${this.generateScripts(config)} - ${this.generateStyles(config)} - -`; - - return html; - } - - /** - * Validate required configuration properties - */ - validateConfig(config) { -const required = ['name', 'title', 'category', 'cssPath', 'jsPath', 'githubPath', 'problem']; - const missing = required.filter(prop => !config[prop]); - - if (missing.length > 0) { - throw new Error(`Missing required config properties: ${missing.join(', ')}`); - } - - if (!config.inputs || !Array.isArray(config.inputs)) { - throw new Error('Config must include inputs array'); - } - - if (!config.explanation || !config.explanation.description) { - throw new Error('Config must include explanation with description'); - } - if (typeof config.problem !== 'string' || !config.problem.trim()) { - throw new Error('Config must include a non-empty string for problem'); - } - - } - - /** - * Generate header section - */ - generateHeader(config) { - return ` -
- - -
`; - } - - /** - * Generate hero section - */ - generateHeroSection(config) { - return ` -
-

Demo - ${config.name}

-
`; - } - - /** - * Generate problem description section - */ - generateProblemSection(config) { - return ` -
-

Problem

-

${config.problem}

-
`; - } - - /** - * Generate theme toggle button - */ - generateThemeToggle() { - return ` - -
- -
`; - } - - /** - * Generate input section based on config - */ - generateInputSection(config) { - const inputElements = config.inputs.map(input => this.generateInputElement(input)).join('\n '); - - return ` -
-

Inputs

-
- ${inputElements} -
- -
-
- ${config.example ? this.generateExampleBox(config.example) : ''} -
`; - } - - /** - * Generate individual input element - */ - generateInputElement(input) { - const commonStyles = 'padding: 8px; border: 1px solid #ddd; border-radius: 4px;'; - - switch (input.type) { - case 'number': - return ` -
- - -
`; - case 'text': - return ` -
- - -
`; - case 'range': - return ` -
- - -
`; - default: - return ``; - } - } - - /** - * Generate example box if provided - */ - generateExampleBox(example) { - return ` -
- Example: ${example} -
`; - } - - /** - * Generate output section - */ - generateOutputSection(config) { - return ` -
-

Result

- -
-
`; - } - - /** - * Generate visualization section if enabled - */ - generateVisualizationSection(config) { - return ` - `; - } - - /** - * Generate source code section - */ - generateSourceCodeSection(config) { - return ` - -
-

Source Code

-

View the complete implementation on GitHub:

- 📂 View on GitHub -
`; - } - - /** - * Generate footer section - */ - generateFooter() { - return ` -
- - -
`; - } - - /** - * Generate explanation accordion section - */ - generateExplanationSection(config) { - const steps = config.explanation.steps ? - config.explanation.steps.map(step => `
  • ${step}
  • `).join('\n ') : - ''; - - return ` - -
    -
    -

    How it works

    - -
    -
    -

    ${config.explanation.description}

    - ${steps ? `
      \n ${steps}\n
    ` : ''} -
    -
    `; - } - - /** - * Generate JavaScript section - */ - generateScripts(config) { - return ` - - - - `; - } - - /** - * Generate main demo function based on config - */ - generateDemoFunction(config) { - // This will be algorithm-specific, so we'll generate a basic template - // and allow configs to override with custom logic - if (config.customDemoFunction) { - return config.customDemoFunction; - } - - // Generate basic input gathering and validation - const inputGathering = config.inputs.map(input => - `const ${input.id.replace('-', '_')} = ${input.type === 'number' ? 'parseInt(' : ''}document.getElementById('${input.id}').value${input.type === 'number' ? ')' : ''};` - ).join('\n '); - - return ` - function runDemo() { - ${inputGathering} - const resultContainer = document.getElementById('result'); - const errorContainer = document.getElementById('error-message'); - - // Clear previous error - errorContainer.innerHTML = ''; - errorContainer.style.display = 'none'; - - try { - // Call algorithm function - this needs to be implemented in the JS file - const result = ${config.algorithmFunction || 'runAlgorithm'}(${config.inputs.map(input => input.id.replace('-', '_')).join(', ')}); - resultContainer.innerHTML = result; - } catch (error) { - showError(error.message); - } - }`; - } - - /** - * Generate utility functions - */ - generateUtilityFunctions(config) { - return ` - function showError(message) { - const errorContainer = document.getElementById('error-message'); - errorContainer.innerHTML = \`⚠️ \${message}\`; - errorContainer.style.display = 'block'; - } - - function wrapLongText(text) { - // Insert spaces every 50 characters to allow wrapping - return text.replace(/(.{50})/g, '$1 '); - }`; - } - - - /** - * Generate initialization script - */ - generateInitializationScript(config) { - return ` - // Initialize demo - document.addEventListener('DOMContentLoaded', () => { - ${config.autoRun !== false ? '// Generate initial result\n runDemo();' : ''} - }); - - ${config.autoRun !== false ? '// Generate initial result on load\n runDemo();' : ''}`; - } - - /** - * Generate CSS styles - */ - generateStyles(config) { - return ` - `; - } - - /** - * Generate visualization-specific styles - */ - generateVisualizationStyles() { - return ` - /* Visualization styles */ - body.dark-mode #visualization-section { - background-color: #272729 !important; - color: #d7dadc; - } - - body.dark-mode #steps-container > div { - background-color: #343536 !important; - border-color: #555 !important; - color: #d7dadc !important; - } - - body.dark-mode #steps-container h4 { - color: #d7dadc !important; - }`; - } -} - -// Export for both Node.js and browser environments -if (typeof module !== 'undefined' && module.exports) { - module.exports = AlgorithmTemplateGenerator; -} else if (typeof window !== 'undefined') { - window.AlgorithmTemplateGenerator = AlgorithmTemplateGenerator; -} diff --git a/algorithms-js/assets/js/components.js b/algorithms-js/assets/js/components.js index b0092892..2e611737 100644 --- a/algorithms-js/assets/js/components.js +++ b/algorithms-js/assets/js/components.js @@ -170,97 +170,98 @@ class AlgorithmList { } loadAlgorithms() { - // This would typically fetch from an API or config file - // For now, we'll define them manually but in a way that's easy to extend + // Use centralized path configuration if available, fallback to hardcoded paths + const paths = window.pathConfig || null; + this.algorithms = [ { name: 'Count and Say', description: 'Generate sequences where each term describes the previous term', - url: 'demo.html?algo=patterns/count-and-say', + url: paths ? paths.algorithms.patterns.COUNT_AND_SAY : 'demo.html?algo=patterns/count-and-say', category: 'sequences', subcategory: 'sequences' }, { name: 'Binary Search', description: 'Efficient search algorithm for sorted arrays with O(log n) complexity', - url: 'demo.html?algo=search/binary-search', + url: paths ? paths.algorithms.search.BINARY_SEARCH : 'demo.html?algo=search/binary-search', category: 'search', subcategory: 'arrays' }, { name: 'Bubble Sort', description: 'Simple sorting algorithm that repeatedly compares adjacent elements', - url: 'demo.html?algo=sort/bubble-sort', + url: paths ? paths.algorithms.sort.BUBBLE_SORT : 'demo.html?algo=sort/bubble-sort', category: 'sort', subcategory: 'comparison' }, { name: 'Selection Sort', description: 'In-place sorting algorithm that finds minimum elements one by one', - url: 'demo.html?algo=sort/selection-sort', + url: paths ? paths.algorithms.sort.SELECTION_SORT : 'demo.html?algo=sort/selection-sort', category: 'sort', subcategory: 'comparison' }, { name: 'Insertion Sort', description: 'Adaptive sorting algorithm that builds sorted array incrementally', - url: 'demo.html?algo=sort/insertion-sort', + url: paths ? paths.algorithms.sort.INSERTION_SORT : 'demo.html?algo=sort/insertion-sort', category: 'sort', subcategory: 'comparison' }, { name: 'Quick Sort', description: 'Efficient divide-and-conquer algorithm with O(n log n) average performance', - url: 'demo.html?algo=sort/quick-sort', + url: paths ? paths.algorithms.sort.QUICK_SORT : 'demo.html?algo=sort/quick-sort', category: 'sort', subcategory: 'divide-and-conquer' }, { name: 'Merge Sort', description: 'Stable divide-and-conquer algorithm with guaranteed O(n log n) time complexity', - url: 'demo.html?algo=sort/merge-sort', + url: paths ? paths.algorithms.sort.MERGE_SORT : 'demo.html?algo=sort/merge-sort', category: 'sort', subcategory: 'divide-and-conquer' }, { name: 'Heap Sort', description: 'In-place sorting algorithm using binary heap with O(n log n) guaranteed performance', - url: 'demo.html?algo=sort/heap-sort', + url: paths ? paths.algorithms.sort.HEAP_SORT : 'demo.html?algo=sort/heap-sort', category: 'sort', subcategory: 'heap-based' }, { name: 'Counting Sort', description: 'Linear-time integer sorting algorithm for small ranges', - url: 'demo.html?algo=sort/counting-sort', + url: paths ? paths.algorithms.sort.COUNTING_SORT : 'demo.html?algo=sort/counting-sort', category: 'sort', subcategory: 'non-comparison' }, { name: 'Radix Sort', description: 'Non-comparison sort that processes digits individually', - url: 'demo.html?algo=sort/radix-sort', + url: paths ? paths.algorithms.sort.RADIX_SORT : 'demo.html?algo=sort/radix-sort', category: 'sort', subcategory: 'non-comparison' }, { name: 'Bucket Sort', description: 'Distribution-based sort for uniformly distributed data', - url: 'demo.html?algo=sort/bucket-sort', + url: paths ? paths.algorithms.sort.BUCKET_SORT : 'demo.html?algo=sort/bucket-sort', category: 'sort', subcategory: 'distribution' }, { name: 'Dutch Flag Sort', description: 'Three-way partitioning algorithm for 2-3 group segregation', - url: 'demo.html?algo=sort/dutch-flag-sort', + url: paths ? paths.algorithms.sort.DUTCH_FLAG_SORT : 'demo.html?algo=sort/dutch-flag-sort', category: 'sort', subcategory: 'partitioning' }, { name: 'Wiggle Sort', description: 'Arranges elements in alternating peak-valley pattern', - url: 'demo.html?algo=sort/wiggle-sort', + url: paths ? paths.algorithms.sort.WIGGLE_SORT : 'demo.html?algo=sort/wiggle-sort', category: 'sort', subcategory: 'specialized' } diff --git a/algorithms-js/assets/js/dynamic-template.js b/algorithms-js/assets/js/dynamic-template.js index 22923751..b24b4f2b 100644 --- a/algorithms-js/assets/js/dynamic-template.js +++ b/algorithms-js/assets/js/dynamic-template.js @@ -1,189 +1,185 @@ /** - * Dynamic Algorithm Template System + * Modular Dynamic Template System - Module Loader & Backward Compatibility * - * This system dynamically generates HTML pages at runtime instead of pre-building them. - * It provides better flexibility and eliminates the need for generated files. + * This file replaces the monolithic dynamic-template.js with a modular architecture. + * It loads all the split modules and provides backward compatibility. + * + * Architecture: + * - PathGenerator: URL and path generation utilities + * - SourceCodeHandler: GitHub links and source code sections + * - TemplateManager: Main orchestrator (replaces DynamicAlgorithmTemplate) + * - HtmlSections: Header, footer, hero, problem sections (embedded) + * - InputGenerators: Input forms and data toggles (embedded) + * - ContentGenerators: Content sections and explanations (embedded) + * - ScriptStyleGenerators: Scripts and styles generation (embedded) + * + * @see https://github.com/sachinlala/SimplifyLearning */ -class DynamicAlgorithmTemplate { - constructor() { - this.baseConfig = this.getBaseConfig(); - } +// Embedded modules to reduce HTTP requests while maintaining separation of concerns - /** - * Get base configuration that's common across all algorithms - */ - getBaseConfig() { - return { - cssPath: "../../../assets/css/styles.css", - componentsPath: "../../../assets/js/components.js", - backPath: "../../../index.html", - hasVisualization: false - }; - } - - /** - * Build asset path based on basePath from config - */ - buildAssetPath(config, relativePath) { +// ===== PATH GENERATOR MODULE ===== +class PathGenerator { + static buildAssetPath(config, relativePath) { if (config.basePath) { return `${config.basePath}/${relativePath}`; } return relativePath; } - /** - * Merge base config with algorithm-specific config - */ - mergeConfig(algorithmConfig) { - return { - ...this.baseConfig, - ...algorithmConfig, - // Auto-generate title from name if not provided - title: algorithmConfig.title || `${algorithmConfig.name} Demo`, - // Ensure required paths are properly set - cssPath: algorithmConfig.cssPath || this.baseConfig.cssPath, - jsPath: algorithmConfig.jsPath || `${algorithmConfig.name.toLowerCase().replace(/\s+/g, '-')}.js`, - githubPath: algorithmConfig.githubPath || this.generateGithubPath(algorithmConfig) - }; - } - - /** - * Auto-generate GitHub path based on algorithm info - * Prefers *-core.js files for better source code focus - */ - generateGithubPath(config) { - // Prefer *-core.js files for better source code focus on algorithm logic - const algorithmSlug = config.name.toLowerCase().replace(/\s+/g, '-'); + static generateGithubPath(config) { + const algorithmSlug = config.name.toLowerCase().replace(/\\s+/g, '-'); const coreFileName = `${algorithmSlug}-core.js`; const fileName = config.jsPath || coreFileName; - - // Use the first category for the GitHub path const primaryCategory = Array.isArray(config.category) ? config.category[0] : config.category; return `https://github.com/sachinlala/SimplifyLearning/blob/master/algorithms-js/src/${primaryCategory}/${algorithmSlug}/${fileName}`; } - /** - * Generate Java source path based on algorithm info - */ - generateJavaPath(config) { - // Use the first category for the Java path + static generateJavaPath(config) { const primaryCategory = Array.isArray(config.category) ? config.category[0] : config.category; - const algorithmName = config.name.toLowerCase().replace(/\s+/g, ''); + const algorithmName = config.name.toLowerCase().replace(/\\s+/g, ''); return `https://github.com/sachinlala/SimplifyLearning/tree/master/algorithms-java/src/main/java/com/sl/algorithms/${primaryCategory}/${algorithmName}`; } - /** - * Check if algorithm is a sorting algorithm (supports both string and array categories) - */ - isSortingAlgorithm(category) { - if (typeof category === 'string') { - return category === 'sort'; - } - if (Array.isArray(category)) { - return category.includes('sort'); - } - return false; - } - - /** - * Generate dynamic algorithms home URL based on config and current environment - */ - generateDynamicAlgorithmsHomeUrl(config) { - // If basePath is provided in config, use it to determine the home URL + static generateDynamicAlgorithmsHomeUrl(config) { if (config.basePath) { return config.basePath + '/'; } - - // For development/relative paths, go back to algorithms index.html return '../../../index.html'; } - /** - * Generate algorithms home URL for navigation (always points to algorithms home, not site root) - */ - generateAlgorithmsHomeUrl(config) { - // Always point to the algorithms index, never to site root + static generateAlgorithmsHomeUrl(config) { if (config.basePath) { return `${config.basePath}/index.html`; } return '../../../index.html'; } - /** - * Generate complete HTML page - */ - generateHTML(algorithmConfig) { - const config = this.mergeConfig(algorithmConfig); - this.validateConfig(config); - - // Build dynamic paths based on basePath - const cssPath = config.basePath ? `${config.basePath}/assets/css/styles.css` : config.cssPath; - const vendorPath = config.basePath ? `${config.basePath}/assets/vendor/prism` : "../../../assets/vendor/prism"; + static generateFaviconPath(config, size = '') { + const basePath = config.basePath || '../../../'; + const sizeStr = size ? `-${size}` : ''; + const extension = size ? '.png' : '.ico'; + return `${basePath}/assets/favicon/favicon${sizeStr}${extension}`; + } + + static generateCSSPath(config) { + return config.basePath ? `${config.basePath}/assets/css/styles.css` : config.cssPath; + } + + static generateVendorPath(config, script = '') { + const basePath = config.basePath || '../../../'; + const scriptPath = script ? `/${script}` : ''; + return `${basePath}/assets/vendor/prism${scriptPath}`; + } + + static generateScriptPaths(config) { + const basePath = config.basePath || '../../../'; + return { + unifiedThemeManager: `${basePath}/assets/js/unified-theme-manager.js`, + sidebar: `${basePath}/assets/js/sidebar.js`, + components: config.componentsPath || `${basePath}/assets/js/components.js`, + utils: `${basePath}/assets/js/utils.js` + }; + } + + static isSortingAlgorithm(category) { + if (typeof category === 'string') { + return category === 'sort'; + } + if (Array.isArray(category)) { + return category.includes('sort'); + } + return false; + } + + static generateLogoPath(config) { + const basePath = config.basePath || '../../../'; + return `${basePath}/assets/images/sl-logo.svg`; + } +} + +// ===== SOURCE CODE HANDLER MODULE ===== +class SourceCodeHandler { + static generateSourceCodeSection(config) { + const sourceCode = config.sourceCode || { + javascript: PathGenerator.generateGithubPath(config), + java: PathGenerator.generateJavaPath(config), + python: "", + go: "" + }; - return ` - - - - - ${config.title} - SimplifyLearning - - - - - - - - - - - - - - - - - ${this.generateHeader(config)} - -
    - ${this.generateHeroSection(config)} - ${this.generateProblemSection(config)} - ${this.generateExplanationSection(config)} + const languages = [ + { + name: 'JavaScript', + icon: ``, + url: sourceCode.javascript, + background: '#fff3cd', + color: '#856404', + border: '#ffeaa7', + enabled: true + }, + { + name: 'Java', + icon: '☕', + url: sourceCode.java, + background: '#ffecd1', + color: '#d68910', + border: '#f8c471', + enabled: !!sourceCode.java + } + ]; -
    - ${this.generateInputSection(config)} - ${this.generateOutputSection(config)} - ${config.hasVisualization ? this.generateVisualizationSection(config) : ''} - ${this.generateSourceCodeSection(config)} -
    -
    - - ${this.generateFooter()} - - ${this.generateScripts(config)} - ${this.generateStyles(config)} - - - - -`; + }).join('\n '); + + return ` + +
    +

    Source Code

    +
    + ${languageLinks} +
    +
    `; + } +} + +// ===== TEMPLATE MANAGER (MAIN ORCHESTRATOR) ===== +class TemplateManager { + constructor() { + this.baseConfig = this.getBaseConfig(); + } + + getBaseConfig() { + return { + cssPath: "../../../assets/css/styles.css", + componentsPath: "../../../assets/js/components.js", + backPath: "../../../index.html", + hasVisualization: false + }; + } + + mergeConfig(algorithmConfig) { + return { + ...this.baseConfig, + ...algorithmConfig, + title: algorithmConfig.title || `${algorithmConfig.name} Demo`, + cssPath: algorithmConfig.cssPath || this.baseConfig.cssPath, + jsPath: algorithmConfig.jsPath || `${algorithmConfig.name.toLowerCase().replace(/\\s+/g, '-')}.js`, + githubPath: algorithmConfig.githubPath || PathGenerator.generateGithubPath(algorithmConfig) + }; } - /** - * Validate required configuration properties - */ validateConfig(config) { const required = ['name', 'category', 'problem']; const missing = required.filter(prop => !config[prop]); @@ -192,31 +188,27 @@ class DynamicAlgorithmTemplate { throw new Error(`Missing required config properties: ${missing.join(', ')}`); } - // Validate category is either string or non-empty array if (Array.isArray(config.category)) { if (config.category.length === 0) { throw new Error('Category array cannot be empty'); } } else if (typeof config.category !== 'string') { - throw new Error('Category must be a string or array of strings'); + throw new Error('Category must be a string or non-empty array'); } - - if (!config.inputs || !Array.isArray(config.inputs)) { - throw new Error('Config must include inputs array'); + } + + isSortingAlgorithm(category) { + if (typeof category === 'string') { + return category === 'sort'; } - - if (!config.explanation || !config.explanation.description) { - throw new Error('Config must include explanation with description'); + if (Array.isArray(category)) { + return category.includes('sort'); } + return false; } - /** - * Generate header section - */ + // Simplified HTML generation methods (embedded for efficiency) generateHeader(config) { - // Generate dynamic SL logo link - const homeUrl = this.generateDynamicAlgorithmsHomeUrl(config); - return `
    @@ -225,8 +217,8 @@ class DynamicAlgorithmTemplate { - - SimplifyLearning + + SimplifyLearning @@ -235,15 +227,15 @@ class DynamicAlgorithmTemplate {
    - + 🏠 Home - ${this.isSortingAlgorithm(config.category) ? `📊 Sorting Algorithms` : ''} + ${this.isSortingAlgorithm(config.category) ? `📊 Sorting Algorithms` : ''}
    + diff --git a/algorithms-js/src/patterns/count-and-say/count-and-say-steps.js b/algorithms-js/src/patterns/count-and-say/count-and-say-steps.js new file mode 100644 index 00000000..22811546 --- /dev/null +++ b/algorithms-js/src/patterns/count-and-say/count-and-say-steps.js @@ -0,0 +1,38 @@ +/** + * Count and Say - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Count and Say visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Count and Say with step-by-step tracking for visualization + * @param {number} startingNumber - The starting number for the sequence + * @param {number} rowNumber - The row number to generate + * @returns {Object} Result with sequence, steps, and metrics + */ +function countAndSayWithSteps(startingNumber, rowNumber) { + // TODO: Implement step-by-step tracking for count and say visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sequence: startingNumber.toString(), + steps: [], + metrics: { iterations: 0, transformations: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + countAndSayWithSteps + }; +} else if (typeof window !== 'undefined') { + window.CountAndSaySteps = { + countAndSayWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.countAndSayWithSteps = countAndSayWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/patterns/count-and-say/count-and-say.config.js b/algorithms-js/src/patterns/count-and-say/count-and-say.config.js index 7b99fde0..46786299 100644 --- a/algorithms-js/src/patterns/count-and-say/count-and-say.config.js +++ b/algorithms-js/src/patterns/count-and-say/count-and-say.config.js @@ -5,7 +5,7 @@ const config = { name: "Count and Say", title: "Count and Say Algorithm Demo", - category: "sequences", + category: "patterns", problem: "Generate the nth term of the Count and Say sequence by reading and describing the digits of the previous term.", cssPath: "../../../assets/css/styles.css", jsPath: "count-and-say-core.js", @@ -20,6 +20,7 @@ const config = { }, algorithmFunction: "generateCountAndSay", hasVisualization: true, + hasStepsFile: true, inputs: [ { diff --git a/algorithms-js/src/search/binary-search/binary-search-core.js b/algorithms-js/src/search/binary-search/binary-search-core.js index 115f8670..b5939459 100644 --- a/algorithms-js/src/search/binary-search/binary-search-core.js +++ b/algorithms-js/src/search/binary-search/binary-search-core.js @@ -119,35 +119,26 @@ function binarySearch(sortedArray, target) { }; } -/** - * Simple binary search function for backward compatibility - * @param {number[]} sortedArray - The sorted array to search in - * @param {number} target - The target element to find - * @returns {number} Index of the target element, or -1 if not found - */ -function binarySearchSimple(sortedArray, target) { - const result = binarySearch(sortedArray, target); - return result.index; -} // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { module.exports = { binarySearch, binarySearchIterative, - binarySearchRecursive, - binarySearchSimple + binarySearchRecursive }; } else if (typeof window !== 'undefined') { window.BinarySearchCore = { binarySearch, binarySearchIterative, - binarySearchRecursive, - binarySearchSimple + binarySearchRecursive }; // Expose commonly used functions for configs window.binarySearchIterative = binarySearchIterative; window.binarySearchRecursive = binarySearchRecursive; // Global function for backward compatibility - window.binarySearch = binarySearchSimple; -} \ No newline at end of file + window.binarySearch = (sortedArray, target) => { + const result = binarySearch(sortedArray, target); + return result.index; + }; +} diff --git a/algorithms-js/src/search/binary-search/binary-search-steps.js b/algorithms-js/src/search/binary-search/binary-search-steps.js new file mode 100644 index 00000000..6e9ff611 --- /dev/null +++ b/algorithms-js/src/search/binary-search/binary-search-steps.js @@ -0,0 +1,39 @@ +/** + * Binary Search - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Binary Search visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Binary Search with step-by-step tracking for visualization + * @param {number[]} arr - Sorted array to search in + * @param {number} target - Target value to search for + * @returns {Object} Result with search result, steps, and metrics + */ +function binarySearchWithSteps(arr, target) { + // TODO: Implement step-by-step tracking for binary search visualization + // This is a placeholder that will be enhanced in future iterations + + return { + found: false, + index: -1, + steps: [], + metrics: { comparisons: 0, iterations: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + binarySearchWithSteps + }; +} else if (typeof window !== 'undefined') { + window.BinarySearchSteps = { + binarySearchWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.binarySearchWithSteps = binarySearchWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/search/binary-search/binary-search.config.js b/algorithms-js/src/search/binary-search/binary-search.config.js index b7b58c35..511c72bd 100644 --- a/algorithms-js/src/search/binary-search/binary-search.config.js +++ b/algorithms-js/src/search/binary-search/binary-search.config.js @@ -9,6 +9,7 @@ const config = { problem: "Given a sorted array, find the index of a target value in O(log n) time.", algorithmFunction: "binarySearchIterativeWithSteps", hasVisualization: true, + hasStepsFile: true, // Multi-language source code paths sourceCode: { @@ -53,17 +54,6 @@ const config = { label: "Target Element", defaultValue: "12", width: "80px" - }, - { - id: "search-method", - type: "select", - label: "Search Method", - options: [ - { value: "iterative", text: "Iterative" }, - { value: "recursive", text: "Recursive" } - ], - defaultValue: "iterative", - width: "150px" } ], @@ -80,7 +70,6 @@ const config = { function runDemo() { const arrayInputStr = document.getElementById('sorted-array').value; const targetElementStr = document.getElementById('target-element').value.trim(); - const searchMethod = document.getElementById('search-method').value; const resultContainer = document.getElementById('result'); const errorContainer = document.getElementById('error-message'); const visualizationSection = document.getElementById('visualization-section'); @@ -135,25 +124,9 @@ const config = { try { const startTime = performance.now(); - let result; - // Execute based on selected method - always with visualization - switch (searchMethod) { - case 'iterative': - const iterativeResult = binarySearchIterativeWithSteps(arrayInput, targetElement); - result = { - ...iterativeResult, - method: 'Iterative' - }; - break; - case 'recursive': - const recursiveResult = binarySearchRecursiveWithSteps(arrayInput, targetElement); - result = { - ...recursiveResult, - method: 'Recursive' - }; - break; - } + // Execute iterative binary search with visualization + const result = binarySearchIterativeWithSteps(arrayInput, targetElement); const endTime = performance.now(); const executionTime = (endTime - startTime).toFixed(4); @@ -163,21 +136,15 @@ const config = { if (result.found) { resultHTML = \` \u003cstrong\u003eElement found at index:\u003c/strong\u003e \${result.index}\u003cbr\u003e - \u003cstrong\u003eMethod:\u003c/strong\u003e \${result.method}\u003cbr\u003e - \u003cstrong\u003eExecution Time:\u003c/strong\u003e \${executionTime} ms\u003cbr\u003e + \u003cstrong\u003eTotal Comparisons:\u003c/strong\u003e \${result.totalComparisons}\u003cbr\u003e + \u003cstrong\u003eExecution Time:\u003c/strong\u003e \${executionTime} ms \`; - if (result.totalComparisons) { - resultHTML += \`\u003cstrong\u003eTotal Comparisons:\u003c/strong\u003e \${result.totalComparisons}\`; - } } else { resultHTML = \` \u003cstrong\u003eElement not found\u003c/strong\u003e\u003cbr\u003e - \u003cstrong\u003eMethod:\u003c/strong\u003e \${result.method}\u003cbr\u003e - \u003cstrong\u003eExecution Time:\u003c/strong\u003e \${executionTime} ms\u003cbr\u003e + \u003cstrong\u003eTotal Comparisons:\u003c/strong\u003e \${result.totalComparisons}\u003cbr\u003e + \u003cstrong\u003eExecution Time:\u003c/strong\u003e \${executionTime} ms \`; - if (result.totalComparisons) { - resultHTML += \`\u003cstrong\u003eTotal Comparisons:\u003c/strong\u003e \${result.totalComparisons}\`; - } } resultContainer.innerHTML = resultHTML; diff --git a/algorithms-js/src/sort/bubble-sort/bubble-sort-core.js b/algorithms-js/src/sort/bubble-sort/bubble-sort-core.js index fc2cccb0..c0d73925 100644 --- a/algorithms-js/src/sort/bubble-sort/bubble-sort-core.js +++ b/algorithms-js/src/sort/bubble-sort/bubble-sort-core.js @@ -14,10 +14,9 @@ /** * Core bubble sort algorithm * @param {number[]} arr - Array to be sorted - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function bubbleSort(arr, options = {}) { +function bubbleSort(arr) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -61,27 +60,11 @@ function bubbleSort(arr, options = {}) { } -/** - * Simple bubble sort function (backward compatibility) - * @param {number[]} arr - Array to sort - * @returns {number[]} Sorted array - */ -function bubbleSortSimple(arr) { - const result = bubbleSort(arr); - return result.sortedArray; -} - // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { - module.exports = { - bubbleSort, - bubbleSortSimple - }; + module.exports = { bubbleSort }; } else if (typeof window !== 'undefined') { - window.BubbleSortCore = { - bubbleSort, - bubbleSortSimple - }; + window.BubbleSortCore = { bubbleSort }; // Global function for backward compatibility - window.bubbleSort = bubbleSortSimple; + window.bubbleSort = arr => bubbleSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/bubble-sort/bubble-sort.config.js b/algorithms-js/src/sort/bubble-sort/bubble-sort.config.js index c83161a5..a485559c 100644 --- a/algorithms-js/src/sort/bubble-sort/bubble-sort.config.js +++ b/algorithms-js/src/sort/bubble-sort/bubble-sort.config.js @@ -19,6 +19,7 @@ const config = { }, algorithmFunction: "bubbleSort", hasVisualization: true, + hasStepsFile: true, // Data type configuration for input toggle inputDataTypes: { @@ -28,7 +29,7 @@ const config = { value: "numeric", label: "Numeric", sampleData: { - "array-input": "64, 34, 25, 12, 22, 11, 90" + "array-input": "64, 34, 25, 12, 22, 11" } }, { @@ -46,7 +47,7 @@ const config = { id: "array-input", type: "text", label: "Array (numbers or strings)", - defaultValue: "64, 34, 25, 12, 22, 11, 90", + defaultValue: "64, 34, 25, 12, 22, 11", width: "300px" }, { diff --git a/algorithms-js/src/sort/bucket-sort/bucket-sort-core.js b/algorithms-js/src/sort/bucket-sort/bucket-sort-core.js index 9dc14e12..4d29b703 100644 --- a/algorithms-js/src/sort/bucket-sort/bucket-sort-core.js +++ b/algorithms-js/src/sort/bucket-sort/bucket-sort-core.js @@ -13,16 +13,19 @@ */ // Import utilities for reusable functions -const SortingUtils = (typeof require !== 'undefined') ? - require('../utils/sorting-utils.js') : window.SortingUtils; +// Note: SortingUtils is available globally via window.SortingUtils when loaded by universal loader +if (typeof require !== 'undefined') { + // Node.js environment + const SortingUtils = require('../utils/sorting-utils.js'); +} +// In browser environment, SortingUtils is available via window.SortingUtils /** * Core bucket sort algorithm for floating-point numbers in range [0, 1) * @param {number[]} arr - Array of numbers to be sorted - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function bucketSort(arr, options = {}) { +function bucketSort(arr) { if (!arr || arr.length === 0) { return { sortedArray: arr || [], @@ -128,16 +131,6 @@ function insertionSortForBucket(arr) { } -/** - * Simple bucket sort function (backward compatibility) - * @param {number[]} arr - Array of numbers to sort - * @returns {number[]} Sorted array - */ -function bucketSortSimple(arr) { - const result = bucketSort(arr); - return result.sortedArray; -} - /** * Bucket sort for integers in a specific range * @param {number[]} arr - Array of integers to sort @@ -173,17 +166,15 @@ function bucketSortIntegers(arr, maxValue = null) { if (typeof module !== 'undefined' && module.exports) { module.exports = { bucketSort, - bucketSortSimple, bucketSortIntegers, insertionSortForBucket }; } else if (typeof window !== 'undefined') { window.BucketSortCore = { bucketSort, - bucketSortSimple, bucketSortIntegers, insertionSortForBucket }; // Global function for backward compatibility - window.bucketSort = bucketSortSimple; + window.bucketSort = arr => bucketSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/bucket-sort/bucket-sort.config.js b/algorithms-js/src/sort/bucket-sort/bucket-sort.config.js index b398b9b2..caaa61d6 100644 --- a/algorithms-js/src/sort/bucket-sort/bucket-sort.config.js +++ b/algorithms-js/src/sort/bucket-sort/bucket-sort.config.js @@ -9,6 +9,8 @@ const BucketSortConfig = { // Top-level properties for dynamic template compatibility name: 'Bucket Sort', category: 'sort', + hasStepsFile: true, + hasVisualization: true, problem: 'Sort an array of floating-point numbers by distributing them into buckets, sorting each bucket individually, then concatenating the results. Most efficient with uniformly distributed data.', // Inputs for the demo interface @@ -18,7 +20,7 @@ const BucketSortConfig = { type: 'text', label: 'Array Elements (decimals 0.0-1.0)', defaultValue: '0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12', - width: '400px' + width: '280px' }, { id: 'bucket-count', @@ -379,7 +381,91 @@ const BucketSortConfig = { ] } ] - } + }, + + customDemoFunction: ` + function runDemo() { + const arrayInputStr = document.getElementById('array-input').value; + const bucketCountStr = document.getElementById('bucket-count').value; + const resultContainer = document.getElementById('result'); + const errorContainer = document.getElementById('error-message'); + const visualizationSection = document.getElementById('visualization-section'); + + // Clear previous error and result + errorContainer.innerHTML = ''; + errorContainer.style.display = 'none'; + resultContainer.innerHTML = ''; + visualizationSection.style.display = 'none'; + + // Parse input array + let arrayInput; + try { + arrayInput = arrayInputStr.split(',').map(item => { + const trimmed = item.trim(); + const asNumber = parseFloat(trimmed); + if (isNaN(asNumber)) { + throw new Error('All elements must be numbers'); + } + return asNumber; + }); + } catch (e) { + showError('Invalid array format. Please use comma-separated decimal numbers (0.0-1.0).'); + return; + } + + // Validate input + if (arrayInput.length === 0) { + showError('Array cannot be empty'); + return; + } + + if (arrayInput.length > 15) { + showError('Array size limited to 15 elements for demo purposes'); + return; + } + + // Validate range + for (let i = 0; i < arrayInput.length; i++) { + if (arrayInput[i] < 0 || arrayInput[i] > 1) { + showError('All elements should be between 0.0 and 1.0 for optimal bucket sort performance'); + return; + } + } + + // Parse bucket count + const bucketCount = parseInt(bucketCountStr); + if (isNaN(bucketCount) || bucketCount < 2 || bucketCount > 10) { + showError('Bucket count must be between 2 and 10'); + return; + } + + try { + const startTime = performance.now(); + + // Execute bucket sort + const result = window.BucketSortCore ? window.BucketSortCore.bucketSort(arrayInput) : bucketSort(arrayInput); + + const endTime = performance.now(); + const executionTime = (endTime - startTime).toFixed(4); + + // Show result + let resultHTML = \` + Original Array: [\${arrayInput.join(', ')}]
    + Sorted Array: [\${result.sortedArray.join(', ')}]
    + Buckets Used: \${result.metrics.buckets}
    + Buckets Sorted: \${result.metrics.bucketSorts}
    + Total Comparisons: \${result.metrics.comparisons}
    + Total Swaps: \${result.metrics.swaps}
    + Execution Time: \${executionTime} ms + \`; + + resultContainer.innerHTML = resultHTML; + + } catch (error) { + showError(error.message); + } + } + ` }; // Export for both Node.js and browser environments diff --git a/algorithms-js/src/sort/counting-sort/counting-sort-core.js b/algorithms-js/src/sort/counting-sort/counting-sort-core.js index 39f5af7a..39e70b53 100644 --- a/algorithms-js/src/sort/counting-sort/counting-sort-core.js +++ b/algorithms-js/src/sort/counting-sort/counting-sort-core.js @@ -18,10 +18,9 @@ /** * Core counting sort algorithm * @param {number[]} arr - Array of non-negative integers to be sorted - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function countingSort(arr, options = {}) { +function countingSort(arr) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -92,27 +91,11 @@ function countingSort(arr, options = {}) { } -/** - * Simple counting sort function (backward compatibility) - * @param {number[]} arr - Array of non-negative integers to sort - * @returns {number[]} Sorted array - */ -function countingSortSimple(arr) { - const result = countingSort(arr); - return result.sortedArray; -} - // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { - module.exports = { - countingSort, - countingSortSimple - }; + module.exports = { countingSort }; } else if (typeof window !== 'undefined') { - window.CountingSortCore = { - countingSort, - countingSortSimple - }; + window.CountingSortCore = { countingSort }; // Global function for backward compatibility - window.countingSort = countingSortSimple; + window.countingSort = arr => countingSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/counting-sort/counting-sort.config.js b/algorithms-js/src/sort/counting-sort/counting-sort.config.js index 0b9fc0d8..8538bf74 100644 --- a/algorithms-js/src/sort/counting-sort/counting-sort.config.js +++ b/algorithms-js/src/sort/counting-sort/counting-sort.config.js @@ -9,6 +9,8 @@ const CountingSortConfig = { // Top-level properties for dynamic template compatibility name: 'Counting Sort', category: 'sort', + hasStepsFile: true, + hasVisualization: true, problem: 'Sort an array of non-negative integers efficiently using counting instead of comparisons. Ideal for integers within a small, known range.', // Inputs for the demo interface @@ -18,7 +20,7 @@ const CountingSortConfig = { type: 'text', label: 'Array Elements (non-negative integers)', defaultValue: '4, 2, 2, 8, 3, 3, 1', - width: '300px' + width: '280px' } ], @@ -308,10 +310,76 @@ const CountingSortConfig = { 'No comparisons needed - faster than comparison-based sorts for suitable inputs', 'Process elements right-to-left in final step to maintain stability' ] - } + }, + + customDemoFunction: ` + function runDemo() { + const arrayInputStr = document.getElementById('array-input').value; + const resultContainer = document.getElementById('result'); + const errorContainer = document.getElementById('error-message'); + const visualizationSection = document.getElementById('visualization-section'); + + // Clear previous error and result + errorContainer.innerHTML = ''; + errorContainer.style.display = 'none'; + resultContainer.innerHTML = ''; + visualizationSection.style.display = 'none'; + + // Parse input array + let arrayInput; + try { + arrayInput = arrayInputStr.split(',').map(item => { + const trimmed = item.trim(); + const asNumber = parseInt(trimmed); + if (isNaN(asNumber) || asNumber < 0) { + throw new Error('All elements must be non-negative integers'); + } + return asNumber; + }); + } catch (e) { + showError('Invalid array format. Please use comma-separated non-negative integers.'); + return; + } + + // Validate input + if (arrayInput.length === 0) { + showError('Array cannot be empty'); + return; + } + + if (arrayInput.length > 20) { + showError('Array size limited to 20 elements for demo purposes'); + return; + } + + try { + const startTime = performance.now(); + + // Execute counting sort + const result = window.CountingSortCore ? window.CountingSortCore.countingSort(arrayInput) : countingSort(arrayInput); + + const endTime = performance.now(); + const executionTime = (endTime - startTime).toFixed(4); + + // Show result + let resultHTML = \` + Original Array: [\${arrayInput.join(', ')}]
    + Sorted Array: [\${result.sortedArray.join(', ')}]
    + Range (k): \${result.metrics.range} (0 to \${result.metrics.maxValue})
    + Space Used: O(n + k) = O(\${arrayInput.length} + \${result.metrics.range})
    + Execution Time: \${executionTime} ms + \`; + + resultContainer.innerHTML = resultHTML; + + } catch (error) { + showError(error.message); + } + } + ` }; -// Export for both Node.js and browser environments +// Export for browser/Node.js environments if (typeof module !== 'undefined' && module.exports) { module.exports = CountingSortConfig; } else if (typeof window !== 'undefined') { diff --git a/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-core.js b/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-core.js index dbc3501e..69f94800 100644 --- a/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-core.js +++ b/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-core.js @@ -14,8 +14,13 @@ */ // Import utilities for reusable functions -const SortingUtils = (typeof require !== 'undefined') ? - require('../utils/sorting-utils.js') : window.SortingUtils; +// Import utilities for reusable functions +// Note: SortingUtils is available globally via window.SortingUtils when loaded by universal loader +if (typeof require !== 'undefined') { + // Node.js environment + const SortingUtils = require('../utils/sorting-utils.js'); +} +// In browser environment, SortingUtils is available via window.SortingUtils /** * Core Dutch National Flag sort algorithm @@ -23,10 +28,9 @@ const SortingUtils = (typeof require !== 'undefined') ? * @param {any} redValue - Value for the first group (red) * @param {any} whiteValue - Value for the middle group (white) - optional * @param {any} blueValue - Value for the third group (blue) - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function dutchFlagSort(arr, redValue, whiteValue, blueValue, options = {}) { +function dutchFlagSort(arr, redValue, whiteValue, blueValue) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -129,17 +133,6 @@ function dutchFlagSort2Way(arr, firstValue, secondValue) { * @param {any} blueValue - Blue group value * @returns {Object} Result with sorted array, steps, and metrics */ - * Simple Dutch flag sort function (backward compatibility) - * @param {any[]} arr - Array to sort - * @param {any} redValue - Red value - * @param {any} whiteValue - White value (optional) - * @param {any} blueValue - Blue value - * @returns {any[]} Sorted array - */ -function dutchFlagSortSimple(arr, redValue, whiteValue, blueValue) { - const result = dutchFlagSort(arr, redValue, whiteValue, blueValue); - return result.sortedArray; -} /** * Sort colors (classic Dutch flag problem) @@ -165,8 +158,6 @@ if (typeof module !== 'undefined' && module.exports) { dutchFlagSort, dutchFlagSort3Way, dutchFlagSort2Way, - - dutchFlagSortSimple, sortColors, sort012 }; @@ -175,13 +166,12 @@ if (typeof module !== 'undefined' && module.exports) { dutchFlagSort, dutchFlagSort3Way, dutchFlagSort2Way, - - dutchFlagSortSimple, sortColors, sort012 }; - // Expose commonly used functions in global scope for demo configs - // Backward compatibility - window.dutchFlagSort = dutchFlagSortSimple; -} \ No newline at end of file + window.dutchFlagSort = (arr, redValue, whiteValue, blueValue) => { + const result = dutchFlagSort(arr, redValue, whiteValue, blueValue); + return result.sortedArray; + }; +} diff --git a/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-steps.js b/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-steps.js new file mode 100644 index 00000000..2f194b9e --- /dev/null +++ b/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Dutch Flag Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Dutch Flag Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Dutch Flag Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function dutchFlagSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Dutch Flag Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, swaps: 0, partitions: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + dutchFlagSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.DutchFlagSortSteps = { + dutchFlagSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.dutchFlagSortWithSteps = dutchFlagSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort.config.js b/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort.config.js index 1bcc5d20..1b820f2b 100644 --- a/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort.config.js +++ b/algorithms-js/src/sort/dutch-flag-sort/dutch-flag-sort.config.js @@ -10,7 +10,9 @@ const DUTCH_FLAG_SORT_CONFIG = { // Top-level properties for dynamic template compatibility name: "Dutch National Flag Sort", - category: "sort", + category: "sort", + hasStepsFile: true, + hasVisualization: true, problem: "Efficiently partition an array into 2-3 distinct groups in linear time using the Dutch National Flag algorithm invented by Edsger Dijkstra.", inputs: [ @@ -19,28 +21,28 @@ const DUTCH_FLAG_SORT_CONFIG = { type: "text", label: "Array Elements", defaultValue: "2, 0, 1, 2, 1, 0", - width: "300px" + width: "200px" }, { id: "red-value", type: "text", - label: "Red Value (First Group)", + label: "Red Value", defaultValue: "0", - width: "100px" + width: "80px" }, { id: "white-value", type: "text", - label: "White Value (Middle Group)", + label: "White Value", defaultValue: "1", - width: "100px" + width: "80px" }, { id: "blue-value", type: "text", - label: "Blue Value (Last Group)", + label: "Blue Value", defaultValue: "2", - width: "100px" + width: "80px" } ], @@ -382,6 +384,93 @@ const DutchFlagSortConfigUtils = { } }; +// Add custom demo function to the config +DUTCH_FLAG_SORT_CONFIG.customDemoFunction = ` + function runDemo() { + const arrayInputStr = document.getElementById('array-input').value; + const redValueStr = document.getElementById('red-value').value; + const blueValueStr = document.getElementById('blue-value').value; + const resultContainer = document.getElementById('result'); + const errorContainer = document.getElementById('error-message'); + const visualizationSection = document.getElementById('visualization-section'); + + // Clear previous error and result + errorContainer.innerHTML = ''; + errorContainer.style.display = 'none'; + resultContainer.innerHTML = ''; + visualizationSection.style.display = 'none'; + + // Parse input array + let arrayInput; + try { + arrayInput = arrayInputStr.split(',').map(item => { + const trimmed = item.trim(); + const asNumber = parseInt(trimmed); + if (isNaN(asNumber)) { + throw new Error('All elements must be integers'); + } + return asNumber; + }); + } catch (e) { + showError('Invalid array format. Please use comma-separated integers.'); + return; + } + + // Parse partition values + const redValue = parseInt(redValueStr); + const blueValue = parseInt(blueValueStr); + + if (isNaN(redValue) || isNaN(blueValue)) { + showError('Red and Blue values must be integers'); + return; + } + + if (redValue >= blueValue) { + showError('Red value must be less than Blue value'); + return; + } + + // Validate input + if (arrayInput.length === 0) { + showError('Array cannot be empty'); + return; + } + + if (arrayInput.length > 20) { + showError('Array size limited to 20 elements for demo purposes'); + return; + } + + try { + const startTime = performance.now(); + + // Execute dutch flag sort + const result = window.DutchFlagSortCore ? + window.DutchFlagSortCore.dutchFlagSort(arrayInput, redValue, blueValue) : + dutchFlagSort(arrayInput, redValue, blueValue); + + const endTime = performance.now(); + const executionTime = (endTime - startTime).toFixed(4); + + // Show result + let resultHTML = \` + Original Array: [\${arrayInput.join(', ')}]
    + Partitioned Array: [\${result.sortedArray.join(', ')}]
    + Red Group (\${redValue}): positions 0 to \${result.metrics.redEnd || 'N/A'}
    + White/Other Group: middle section
    + Blue Group (\${blueValue}): end section
    + Total Swaps: \${result.metrics.swaps || 0}
    + Execution Time: \${executionTime} ms + \`; + + resultContainer.innerHTML = resultHTML; + + } catch (error) { + showError(error.message); + } + } +`; + // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { module.exports = { diff --git a/algorithms-js/src/sort/heap-sort/heap-sort-steps.js b/algorithms-js/src/sort/heap-sort/heap-sort-steps.js new file mode 100644 index 00000000..38f58a48 --- /dev/null +++ b/algorithms-js/src/sort/heap-sort/heap-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Heap Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Heap Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Heap Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function heapSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Heap Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, swaps: 0, heapifications: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + heapSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.HeapSortSteps = { + heapSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.heapSortWithSteps = heapSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/heap-sort/heap-sort.config.js b/algorithms-js/src/sort/heap-sort/heap-sort.config.js index 89dda554..c315f56e 100644 --- a/algorithms-js/src/sort/heap-sort/heap-sort.config.js +++ b/algorithms-js/src/sort/heap-sort/heap-sort.config.js @@ -9,6 +9,7 @@ const config = { problem: "Sort an array using the heap-based sorting algorithm with guaranteed O(n log n) time complexity and in-place operation.", algorithmFunction: "runHeapSortDemo", hasVisualization: true, + hasStepsFile: true, // Multi-language source code paths sourceCode: { diff --git a/algorithms-js/src/sort/insertion-sort/insertion-sort-core.js b/algorithms-js/src/sort/insertion-sort/insertion-sort-core.js index 32342ea7..fcaa7f51 100644 --- a/algorithms-js/src/sort/insertion-sort/insertion-sort-core.js +++ b/algorithms-js/src/sort/insertion-sort/insertion-sort-core.js @@ -20,10 +20,9 @@ /** * Core insertion sort algorithm * @param {number[]} arr - Array to be sorted - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function insertionSort(arr, options = {}) { +function insertionSort(arr) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -116,31 +115,19 @@ function binaryInsertionSort(arr) { }; } -/** - * Simple insertion sort function (backward compatibility) - * @param {number[]} arr - Array to sort - * @returns {number[]} Sorted array - */ -function insertionSortSimple(arr) { - const result = insertionSort(arr); - return result.sortedArray; -} - // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { module.exports = { insertionSort, - binaryInsertionSort, - insertionSortSimple + binaryInsertionSort }; } else if (typeof window !== 'undefined') { window.InsertionSortCore = { insertionSort, - binaryInsertionSort, - insertionSortSimple + binaryInsertionSort }; // Expose commonly used functions for configs window.binaryInsertionSort = binaryInsertionSort; // Global function for backward compatibility - window.insertionSort = insertionSortSimple; + window.insertionSort = arr => insertionSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/insertion-sort/insertion-sort-steps.js b/algorithms-js/src/sort/insertion-sort/insertion-sort-steps.js new file mode 100644 index 00000000..77936509 --- /dev/null +++ b/algorithms-js/src/sort/insertion-sort/insertion-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Insertion Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Insertion Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Insertion Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function insertionSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Insertion Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, insertions: 0, shifts: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + insertionSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.InsertionSortSteps = { + insertionSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.insertionSortWithSteps = insertionSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/insertion-sort/insertion-sort.config.js b/algorithms-js/src/sort/insertion-sort/insertion-sort.config.js index 8dcbff86..9a9c3166 100644 --- a/algorithms-js/src/sort/insertion-sort/insertion-sort.config.js +++ b/algorithms-js/src/sort/insertion-sort/insertion-sort.config.js @@ -9,6 +9,7 @@ const config = { problem: "Sort an array by building the final sorted array one element at a time, inserting each element at its correct position in the already sorted portion.", algorithmFunction: "insertionSortWithSteps", hasVisualization: true, + hasStepsFile: true, // Multi-language source code paths sourceCode: { @@ -29,8 +30,8 @@ const config = { type: "select", label: "Algorithm Variant", options: [ - { value: "standard", text: "Standard Insertion Sort" }, - { value: "binary", text: "Binary Insertion Sort" } + { value: "standard", text: "Standard" }, + { value: "binary", text: "Binary" } ], defaultValue: "standard", width: "180px" @@ -368,18 +369,13 @@ const config = { // Create array visualization const arrayDiv = document.createElement('div'); - arrayDiv.style.cssText = 'display: flex; gap: 3px; justify-content: center; margin-bottom: 20px; flex-wrap: wrap;'; + arrayDiv.className = 'array-visualization'; arrayDiv.id = 'binary-sort-array-display'; originalArray.forEach((value, index) => { const cell = document.createElement('div'); cell.textContent = value; - cell.style.cssText = \` - width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; - border: 2px solid #ddd; background: #f8f9fa; font-weight: bold; - border-radius: 4px; margin: 2px; transition: all 0.6s ease; - font-size: 14px; position: relative; - \`; + cell.className = 'viz-cell'; cell.setAttribute('data-index', index); cell.setAttribute('data-value', value); arrayDiv.appendChild(cell); @@ -391,16 +387,43 @@ const config = { const controlsDiv = document.createElement('div'); controlsDiv.style.cssText = 'text-align: center; margin-bottom: 20px;'; controlsDiv.innerHTML = \` -
    Binary Insertion Sort Visualization
    - - - -
    - Green: Sorted | Orange: Current element | Purple: Binary search range | Blue: Mid element +

    Binary Insertion Sort Visualization

    + + + +
    + 🟢 Sorted | 🟠 Current | 🟣 Search Range | 🔵 Mid | 🔴 Shifting +
    \`; arrayViz.appendChild(controlsDiv); + // Toggle legend display based on screen size + function updateBinaryLegendDisplay() { + const isMobile = window.innerWidth <= 768; + const desktopLegend = document.querySelector('#binaryinsertionsort-legend .viz-legend-desktop'); + const mobileLegend = document.querySelector('#binaryinsertionsort-legend .viz-legend-mobile'); + + if (desktopLegend && mobileLegend) { + if (isMobile) { + desktopLegend.style.display = 'none'; + mobileLegend.style.display = 'flex'; + } else { + desktopLegend.style.display = 'inline'; + mobileLegend.style.display = 'none'; + } + } + } + + updateBinaryLegendDisplay(); + window.addEventListener('resize', updateBinaryLegendDisplay); + // Status display const statusDiv = document.createElement('div'); statusDiv.id = 'binary-sort-status'; @@ -414,15 +437,12 @@ const config = { let animationInterval; function updateBinaryVisualization(step) { - const cells = arrayDiv.querySelectorAll('div'); + const cells = arrayDiv.querySelectorAll('.viz-cell'); const statusDiv = document.getElementById('binary-sort-status'); - // Reset all cell colors + // Reset all cell classes cells.forEach(cell => { - cell.style.background = '#f8f9fa'; - cell.style.borderColor = '#ddd'; - cell.style.transform = 'scale(1)'; - cell.style.zIndex = '1'; + cell.className = 'viz-cell'; }); // Update array values @@ -436,52 +456,40 @@ const config = { if (step.sortedUpTo) { for (let i = 0; i < step.sortedUpTo; i++) { if (cells[i]) { - cells[i].style.background = '#c8e6c9'; - cells[i].style.borderColor = '#4caf50'; + cells[i].className = 'viz-cell sorted'; } } } // Highlight current element being processed (orange) if (step.currentIndex !== undefined && cells[step.currentIndex]) { - cells[step.currentIndex].style.background = '#fff3e0'; - cells[step.currentIndex].style.borderColor = '#ff9800'; - cells[step.currentIndex].style.transform = 'scale(1.1)'; - cells[step.currentIndex].style.zIndex = '10'; + cells[step.currentIndex].className = 'viz-cell current'; } - // Highlight binary search range (purple/violet) + // Highlight binary search range (purple) 🟣 if (step.searchRange && step.type.includes('binary-search')) { for (let i = step.searchRange[0]; i <= step.searchRange[1]; i++) { if (cells[i] && i !== step.currentIndex && i !== step.mid) { - cells[i].style.background = '#f3e5f5'; - cells[i].style.borderColor = '#9c27b0'; + cells[i].className = 'viz-cell swapping'; // Purple/magenta - rgba(156, 39, 176, 0.8) } } } - // Highlight mid element in binary search (blue) + // Highlight mid element in binary search (blue) 🔵 - override other classes if (step.mid !== undefined && cells[step.mid]) { - cells[step.mid].style.background = '#e3f2fd'; - cells[step.mid].style.borderColor = '#2196f3'; - cells[step.mid].style.transform = 'scale(1.15)'; - cells[step.mid].style.zIndex = '8'; + cells[step.mid].className = 'viz-cell current-range'; // Blue - rgba(33, 150, 243, 0.6) } - // Highlight insertion position (bright green) + // Highlight insertion position - use distinct green if (step.insertPosition !== undefined && cells[step.insertPosition] && step.type === 'position-found') { - cells[step.insertPosition].style.background = '#a5d6a7'; - cells[step.insertPosition].style.borderColor = '#388e3c'; - cells[step.insertPosition].style.transform = 'scale(1.1)'; - cells[step.insertPosition].style.zIndex = '9'; + cells[step.insertPosition].className = 'viz-cell just-inserted'; // Bright purple for insertion } - // Highlight shifting range (light red) + // Highlight shifting range (red) 🔴 if (step.shiftRange && (step.type === 'shift-start' || step.type === 'shift-complete')) { for (let i = step.shiftRange[0]; i <= step.shiftRange[1]; i++) { if (cells[i] && i !== step.currentIndex) { - cells[i].style.background = '#ffcdd2'; - cells[i].style.borderColor = '#f44336'; + cells[i].className = 'viz-cell pivot'; // Red - rgba(244, 67, 54, 0.85) } } } @@ -491,18 +499,18 @@ const config = { // Show step info in container const stepInfo = document.createElement('div'); - stepInfo.style.cssText = 'background: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 4px; font-size: 0.9em; border-left: 4px solid #007acc;'; - // Color code the step info based on step type + // Use CSS classes that adapt to theme instead of hardcoded colors if (step.type.includes('binary-search')) { - stepInfo.style.borderLeftColor = '#9c27b0'; - stepInfo.style.background = '#f3e5f5'; + stepInfo.className = 'viz-step-info binary-search'; } else if (step.type === 'insert') { - stepInfo.style.borderLeftColor = '#4caf50'; - stepInfo.style.background = '#e8f5e8'; + stepInfo.className = 'viz-step-info insert'; } else if (step.type.includes('shift')) { - stepInfo.style.borderLeftColor = '#f44336'; - stepInfo.style.background = '#ffebee'; + stepInfo.className = 'viz-step-info shift'; + } else if (step.type === 'complete') { + stepInfo.className = 'viz-step-info complete'; + } else { + stepInfo.className = 'viz-step-info'; } stepInfo.innerHTML = \` @@ -510,11 +518,6 @@ const config = { Comparisons: \${step.comparisons || 0}, Shifts: \${step.shifts || 0} \`; - if (step.type === 'complete') { - stepInfo.style.borderLeftColor = '#28a745'; - stepInfo.style.background = '#d4edda'; - } - if (stepsContainer.children.length > 8) { stepsContainer.removeChild(stepsContainer.firstChild); } diff --git a/algorithms-js/src/sort/merge-sort/merge-sort-steps.js b/algorithms-js/src/sort/merge-sort/merge-sort-steps.js new file mode 100644 index 00000000..dbee3444 --- /dev/null +++ b/algorithms-js/src/sort/merge-sort/merge-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Merge Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Merge Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Merge Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function mergeSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Merge Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, merges: 0, recursiveDepth: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + mergeSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.MergeSortSteps = { + mergeSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.mergeSortWithSteps = mergeSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/merge-sort/merge-sort.config.js b/algorithms-js/src/sort/merge-sort/merge-sort.config.js index e5b4c3c2..beb7b7fc 100644 --- a/algorithms-js/src/sort/merge-sort/merge-sort.config.js +++ b/algorithms-js/src/sort/merge-sort/merge-sort.config.js @@ -9,6 +9,7 @@ const config = { problem: "Sort an array using the stable divide-and-conquer merge sort algorithm with guaranteed O(n log n) time complexity.", algorithmFunction: "runMergeSortDemo", hasVisualization: true, + hasStepsFile: true, // Multi-language source code paths sourceCode: { diff --git a/algorithms-js/src/sort/quick-sort/quick-sort-core.js b/algorithms-js/src/sort/quick-sort/quick-sort-core.js index d708c311..9a6e9c29 100644 --- a/algorithms-js/src/sort/quick-sort/quick-sort-core.js +++ b/algorithms-js/src/sort/quick-sort/quick-sort-core.js @@ -207,31 +207,19 @@ function partitionSimple(array, low, high, metrics) { return i + 1; } -/** - * Simple quick sort function (backward compatibility) - * @param {number[]} arr - Array to sort - * @returns {number[]} Sorted array - */ -function quickSortSimple(arr) { - const result = quickSort(arr); - return result.sortedArray; -} - // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { module.exports = { quickSort, - quickSortIterative, - quickSortSimple + quickSortIterative }; } else if (typeof window !== 'undefined') { window.QuickSortCore = { quickSort, - quickSortIterative, - quickSortSimple + quickSortIterative }; // Expose commonly used functions for configs window.quickSortIterative = quickSortIterative; // Global function for backward compatibility - window.quickSort = quickSortSimple; + window.quickSort = arr => quickSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/quick-sort/quick-sort-steps.js b/algorithms-js/src/sort/quick-sort/quick-sort-steps.js new file mode 100644 index 00000000..cfe91ff3 --- /dev/null +++ b/algorithms-js/src/sort/quick-sort/quick-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Quick Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Quick Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Quick Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function quickSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Quick Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, swaps: 0, partitions: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + quickSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.QuickSortSteps = { + quickSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.quickSortWithSteps = quickSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/quick-sort/quick-sort.config.js b/algorithms-js/src/sort/quick-sort/quick-sort.config.js index 3327e6cd..e7638667 100644 --- a/algorithms-js/src/sort/quick-sort/quick-sort.config.js +++ b/algorithms-js/src/sort/quick-sort/quick-sort.config.js @@ -9,6 +9,7 @@ const config = { problem: "Sort an array efficiently using the divide-and-conquer approach by partitioning around a pivot element.", algorithmFunction: "quickSortWithSteps", hasVisualization: true, + hasStepsFile: true, // Multi-language source code paths sourceCode: { diff --git a/algorithms-js/src/sort/radix-sort/radix-sort-core.js b/algorithms-js/src/sort/radix-sort/radix-sort-core.js index efaf8c32..04dcb3c1 100644 --- a/algorithms-js/src/sort/radix-sort/radix-sort-core.js +++ b/algorithms-js/src/sort/radix-sort/radix-sort-core.js @@ -19,10 +19,9 @@ const DECIMAL_RADIX = 10; /** * Core radix sort algorithm * @param {number[]} arr - Array of non-negative integers to be sorted - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function radixSort(arr, options = {}) { +function radixSort(arr) { if (!arr || arr.length === 0) { return { sortedArray: arr || [], @@ -113,26 +112,17 @@ function countingSort(arr, digitPlace) { } } -/** - * Simple radix sort function (backward compatibility) - * @param {number[]} arr - Array of non-negative integers to sort - * @returns {number[]} Sorted array - */ -function radixSortSimple(arr) { - // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { module.exports = { radixSort, - radixSortSimple, countingSort }; } else if (typeof window !== 'undefined') { window.RadixSortCore = { radixSort, - radixSortSimple, countingSort }; // Global function for backward compatibility - window.radixSort = radixSortSimple; + window.radixSort = arr => radixSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/radix-sort/radix-sort.config.js b/algorithms-js/src/sort/radix-sort/radix-sort.config.js index ccf903da..338335ac 100644 --- a/algorithms-js/src/sort/radix-sort/radix-sort.config.js +++ b/algorithms-js/src/sort/radix-sort/radix-sort.config.js @@ -9,6 +9,8 @@ const RadixSortConfig = { // Top-level properties for dynamic template compatibility name: 'Radix Sort', category: 'sort', + hasStepsFile: true, + hasVisualization: true, problem: 'Sort an array of non-negative integers efficiently by processing digits from least significant to most significant, using counting sort as a stable subroutine for each digit position.', // Inputs for the demo interface @@ -18,7 +20,7 @@ const RadixSortConfig = { type: 'text', label: 'Array Elements (non-negative integers)', defaultValue: '170, 45, 75, 90, 2, 802, 24, 66', - width: '350px' + width: '280px' } ], @@ -323,7 +325,74 @@ const RadixSortConfig = { ] } ] - } + }, + + customDemoFunction: ` + function runDemo() { + const arrayInputStr = document.getElementById('array-input').value; + const resultContainer = document.getElementById('result'); + const errorContainer = document.getElementById('error-message'); + const visualizationSection = document.getElementById('visualization-section'); + + // Clear previous error and result + errorContainer.innerHTML = ''; + errorContainer.style.display = 'none'; + resultContainer.innerHTML = ''; + visualizationSection.style.display = 'none'; + + // Parse input array + let arrayInput; + try { + arrayInput = arrayInputStr.split(',').map(item => { + const trimmed = item.trim(); + const asNumber = parseInt(trimmed); + if (isNaN(asNumber) || asNumber < 0) { + throw new Error('All elements must be non-negative integers'); + } + return asNumber; + }); + } catch (e) { + showError('Invalid array format. Please use comma-separated non-negative integers.'); + return; + } + + // Validate input + if (arrayInput.length === 0) { + showError('Array cannot be empty'); + return; + } + + if (arrayInput.length > 15) { + showError('Array size limited to 15 elements for demo purposes'); + return; + } + + try { + const startTime = performance.now(); + + // Execute radix sort + const result = window.RadixSortCore ? window.RadixSortCore.radixSort(arrayInput) : radixSort(arrayInput); + + const endTime = performance.now(); + const executionTime = (endTime - startTime).toFixed(4); + + // Show result + let resultHTML = \` + Original Array: [\${arrayInput.join(', ')}]
    + Sorted Array: [\${result.sortedArray.join(', ')}]
    + Max Digits (d): \${result.metrics.maxDigits}
    + Passes: \${result.metrics.passes}
    + Total Operations: \${result.metrics.totalOperations}
    + Execution Time: \${executionTime} ms + \`; + + resultContainer.innerHTML = resultHTML; + + } catch (error) { + showError(error.message); + } + } + ` }; // Export for both Node.js and browser environments diff --git a/algorithms-js/src/sort/selection-sort/selection-sort-core.js b/algorithms-js/src/sort/selection-sort/selection-sort-core.js index 1ac8bc0f..ebb4fe43 100644 --- a/algorithms-js/src/sort/selection-sort/selection-sort-core.js +++ b/algorithms-js/src/sort/selection-sort/selection-sort-core.js @@ -18,10 +18,9 @@ /** * Core selection sort algorithm * @param {number[]} arr - Array to be sorted - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function selectionSort(arr, options = {}) { +function selectionSort(arr) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -60,27 +59,11 @@ function selectionSort(arr, options = {}) { }; } -/** - * Simple selection sort function (backward compatibility) - * @param {number[]} arr - Array to sort - * @returns {number[]} Sorted array - */ -function selectionSortSimple(arr) { - const result = selectionSort(arr); - return result.sortedArray; -} - // Export for both Node.js and browser environments if (typeof module !== 'undefined' && module.exports) { - module.exports = { - selectionSort, - selectionSortSimple - }; + module.exports = { selectionSort }; } else if (typeof window !== 'undefined') { - window.SelectionSortCore = { - selectionSort, - selectionSortSimple - }; + window.SelectionSortCore = { selectionSort }; // Global function for backward compatibility - window.selectionSort = selectionSortSimple; + window.selectionSort = arr => selectionSort(arr).sortedArray; } diff --git a/algorithms-js/src/sort/selection-sort/selection-sort-steps.js b/algorithms-js/src/sort/selection-sort/selection-sort-steps.js new file mode 100644 index 00000000..b93b2477 --- /dev/null +++ b/algorithms-js/src/sort/selection-sort/selection-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Selection Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Selection Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Selection Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function selectionSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Selection Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, swaps: 0, selections: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + selectionSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.SelectionSortSteps = { + selectionSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.selectionSortWithSteps = selectionSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/selection-sort/selection-sort.config.js b/algorithms-js/src/sort/selection-sort/selection-sort.config.js index 170c62c0..3ee8a7ac 100644 --- a/algorithms-js/src/sort/selection-sort/selection-sort.config.js +++ b/algorithms-js/src/sort/selection-sort/selection-sort.config.js @@ -9,6 +9,7 @@ const config = { problem: "Sort an array by repeatedly finding the minimum element from the unsorted part and putting it at the beginning.", algorithmFunction: "selectionSortWithSteps", hasVisualization: true, + hasStepsFile: true, // Multi-language source code paths sourceCode: { diff --git a/algorithms-js/src/sort/wiggle-sort/wiggle-sort-core.js b/algorithms-js/src/sort/wiggle-sort/wiggle-sort-core.js index a25d4131..cdea0406 100644 --- a/algorithms-js/src/sort/wiggle-sort/wiggle-sort-core.js +++ b/algorithms-js/src/sort/wiggle-sort/wiggle-sort-core.js @@ -15,17 +15,21 @@ */ // Import utilities for reusable functions -const SortingUtils = (typeof require !== 'undefined') ? - require('../utils/sorting-utils.js') : window.SortingUtils; +// Import utilities for reusable functions +// Note: SortingUtils is available globally via window.SortingUtils when loaded by universal loader +if (typeof require !== 'undefined') { + // Node.js environment + const SortingUtils = require('../utils/sorting-utils.js'); +} +// In browser environment, SortingUtils is available via window.SortingUtils /** * Wiggle Sort I - In-place O(n) algorithm * Ensures arr[0] < arr[1] > arr[2] < arr[3]... pattern * @param {number[]} arr - Array to wiggle sort - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function wiggleSortI(arr, options = {}) { +function wiggleSortI(arr) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -67,10 +71,9 @@ function wiggleSortI(arr, options = {}) { * Wiggle Sort II - No adjacent duplicates * Uses sorting and rearrangement to ensure no adjacent duplicates * @param {number[]} arr - Array to wiggle sort - * @param {Object} options - Options for sorting behavior * @returns {Object} Sorted array and metrics */ -function wiggleSortII(arr, options = {}) { +function wiggleSortII(arr) { if (!arr || arr.length <= 1) { return { sortedArray: arr || [], @@ -115,19 +118,82 @@ function wiggleSortII(arr, options = {}) { } /** - * Wiggle Sort with step-by-step tracking for visualization - * @param {number[]} arr - Array to be sorted - * @param {string} variant - "I" for wiggle sort I, "II" for wiggle sort II - * @returns {Object} Result with sorted array, steps, and metrics + * Check if an array is in wiggle sorted order + * @param {number[]} arr - Array to check + * @returns {boolean} True if array is wiggle sorted */ - * Simple wiggle sort function (backward compatibility) - * @param {number[]} arr - Array to sort - * @param {string} variant - "I" or "II" - * @returns {number[]} Wiggle sorted array +function isWiggleSorted(arr) { + if (!arr || arr.length <= 1) return true; + + for (let i = 0; i < arr.length - 1; i++) { + if (i % 2 === 0) { + // Even index: should be less than next (valley) + if (arr[i] > arr[i + 1]) return false; + } else { + // Odd index: should be greater than next (peak) + if (arr[i] < arr[i + 1]) return false; + } + } + return true; +} + +/** + * Get wiggle pattern description for an array + * @param {number[]} arr - Array to analyze + * @returns {Object} Pattern information */ -function wiggleSortSimple(arr, variant = "I") { - const result = variant === "I" ? wiggleSortI(arr) : wiggleSortII(arr); - return result.sortedArray; +function getWigglePattern(arr) { + if (!arr || arr.length === 0) { + return { pattern: [], isWiggleSorted: true, violations: [] }; + } + + const pattern = []; + const violations = []; + + for (let i = 0; i < arr.length - 1; i++) { + const current = arr[i]; + const next = arr[i + 1]; + const expectedPattern = i % 2 === 0 ? 'valley' : 'peak'; + + if (i % 2 === 0) { + // Even index: should be valley (less than next) + if (current <= next) { + pattern.push({ index: i, type: 'valley', satisfied: true }); + } else { + pattern.push({ index: i, type: 'valley', satisfied: false }); + violations.push({ + index: i, + expected: 'valley (<=)', + actual: `${current} > ${next}`, + type: 'valley_violation' + }); + } + } else { + // Odd index: should be peak (greater than next) + if (current >= next) { + pattern.push({ index: i, type: 'peak', satisfied: true }); + } else { + pattern.push({ index: i, type: 'peak', satisfied: false }); + violations.push({ + index: i, + expected: 'peak (>=)', + actual: `${current} < ${next}`, + type: 'peak_violation' + }); + } + } + } + + return { + pattern, + isWiggleSorted: violations.length === 0, + violations, + summary: { + valleys: pattern.filter(p => p.type === 'valley').length, + peaks: pattern.filter(p => p.type === 'peak').length, + violationsCount: violations.length + } + }; } // Export for both Node.js and browser environments @@ -135,8 +201,6 @@ if (typeof module !== 'undefined' && module.exports) { module.exports = { wiggleSortI, wiggleSortII, - - wiggleSortSimple, isWiggleSorted, getWigglePattern }; @@ -144,13 +208,12 @@ if (typeof module !== 'undefined' && module.exports) { window.WiggleSortCore = { wiggleSortI, wiggleSortII, - - wiggleSortSimple, isWiggleSorted, getWigglePattern }; - // Expose commonly used functions in global scope for demo configs - // Backward compatibility - window.wiggleSort = wiggleSortSimple; -} \ No newline at end of file + window.wiggleSort = (arr, variant = "I") => { + const result = variant === "I" ? wiggleSortI(arr) : wiggleSortII(arr); + return result.sortedArray; + }; +} diff --git a/algorithms-js/src/sort/wiggle-sort/wiggle-sort-steps.js b/algorithms-js/src/sort/wiggle-sort/wiggle-sort-steps.js new file mode 100644 index 00000000..0e6c1a4e --- /dev/null +++ b/algorithms-js/src/sort/wiggle-sort/wiggle-sort-steps.js @@ -0,0 +1,37 @@ +/** + * Wiggle Sort - Step Tracking for Visualization + * + * This file contains step-by-step tracking logic for Wiggle Sort visualization. + * Currently placeholder - to be implemented in future animation updates. + * + * @see https://github.com/sachinlala/SimplifyLearning + */ + +/** + * Wiggle Sort with step-by-step tracking for visualization + * @param {number[]} arr - Array to be sorted + * @returns {Object} Result with sorted array, steps, and metrics + */ +function wiggleSortWithSteps(arr) { + // TODO: Implement step-by-step tracking for Wiggle Sort visualization + // This is a placeholder that will be enhanced in future iterations + + return { + sortedArray: [...arr], + steps: [], + metrics: { comparisons: 0, swaps: 0, wiggleOperations: 0 } + }; +} + +// Export for both Node.js and browser environments +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + wiggleSortWithSteps + }; +} else if (typeof window !== 'undefined') { + window.WiggleSortSteps = { + wiggleSortWithSteps + }; + // Expose commonly used functions in global scope for demo configs + window.wiggleSortWithSteps = wiggleSortWithSteps; +} \ No newline at end of file diff --git a/algorithms-js/src/sort/wiggle-sort/wiggle-sort.config.js b/algorithms-js/src/sort/wiggle-sort/wiggle-sort.config.js index e1bc2000..5205bbf4 100644 --- a/algorithms-js/src/sort/wiggle-sort/wiggle-sort.config.js +++ b/algorithms-js/src/sort/wiggle-sort/wiggle-sort.config.js @@ -11,6 +11,7 @@ const WIGGLE_SORT_CONFIG = { // Top-level properties for dynamic template compatibility name: "Wiggle Sort", category: "sort", + hasStepsFile: true, problem: "Arrange array elements in a wiggling pattern where arr[0] < arr[1] > arr[2] < arr[3] > arr[4]... Elements alternate between peaks and valleys.", // Inputs for the demo interface @@ -387,7 +388,77 @@ const WiggleSortConfigUtils = { violations, isValid: violations === 0 }; - } + }, + + customDemoFunction: ` + function runDemo() { + const arrayInputStr = document.getElementById('array-input').value; + const variant = document.getElementById('variant').value; + const resultContainer = document.getElementById('result'); + const errorContainer = document.getElementById('error-message'); + const visualizationSection = document.getElementById('visualization-section'); + + // Clear previous error and result + errorContainer.innerHTML = ''; + errorContainer.style.display = 'none'; + resultContainer.innerHTML = ''; + visualizationSection.style.display = 'none'; + + // Parse input array + let arrayInput; + try { + arrayInput = arrayInputStr.split(',').map(item => { + const trimmed = item.trim(); + const asNumber = parseInt(trimmed); + if (isNaN(asNumber)) { + throw new Error('All elements must be integers'); + } + return asNumber; + }); + } catch (e) { + showError('Invalid array format. Please use comma-separated integers.'); + return; + } + + // Validate input + if (arrayInput.length === 0) { + showError('Array cannot be empty'); + return; + } + + if (arrayInput.length > 15) { + showError('Array size limited to 15 elements for demo purposes'); + return; + } + + try { + const startTime = performance.now(); + + // Execute wiggle sort + const result = window.WiggleSortCore ? + window.WiggleSortCore.wiggleSort(arrayInput, variant) : + wiggleSort(arrayInput, variant); + + const endTime = performance.now(); + const executionTime = (endTime - startTime).toFixed(4); + + // Show result + let resultHTML = \` + Original Array: [\${arrayInput.join(', ')}]
    + Wiggle Sorted Array: [\${result.sortedArray.join(', ')}]
    + Variant: \${variant}
    + Pattern: \${result.pattern || 'a0 < a1 > a2 < a3 > ...'}
    + Total Swaps: \${result.metrics.swaps || 0}
    + Execution Time: \${executionTime} ms + \`; + + resultContainer.innerHTML = resultHTML; + + } catch (error) { + showError(error.message); + } + } + ` }; // Export for both Node.js and browser environments