QUESTION #026

Sort Three Numbers Using Macros

Easy

📋 Problem Statement

Given 3 integer values, arrange those 3 values in ascending order using macros.

🆕 New Concept: Preprocessor Macros

This problem introduces macros - a powerful C++ preprocessor feature that allows text substitution before compilation. Unlike functions, macros are expanded at compile-time!

🎯 Understanding Macros

  • #define Directive: Used to create macros (preprocessor directives)
  • Text Substitution: Macros replace text before code is compiled
  • No Type Checking: Works with any data type (generic)
  • Inline Expansion: Code is inserted directly, no function call overhead
  • Ternary Operator: condition ? true_value : false_value
Sorting Strategy: Find Min, Max, Calculate Middle
Input: a = 30, b = 10, c = 20

Step 1: Find Minimum
        MIN(a, MIN(b, c))
        MIN(30, MIN(10, 20))
        MIN(30, 10) = 10  ✓

Step 2: Find Maximum
        MAX(a, MAX(b, c))
        MAX(30, MAX(10, 20))
        MAX(30, 20) = 30  ✓

Step 3: Calculate Middle
        mid = a + b + c - min - max
        mid = 30 + 10 + 20 - 10 - 30
        mid = 60 - 40 = 20  ✓

Output: 10 < 20 < 30

📥 Input Format

Accept three integer values as a input

📤 Output Format

Print the output as "min < mid < max"

💡 Examples

Example 1:
Input: 2147483648 21474836 2147483647
Output: 21474836 < 2147483647 < 2147483648
Analysis:
• Values: a = 2147483648, b = 21474836, c = 2147483647
• Minimum: MIN(2147483648, MIN(21474836, 2147483647)) = 21474836
• Maximum: MAX(2147483648, MAX(21474836, 2147483647)) = 2147483648
• Middle: 2147483648 + 21474836 + 2147483647 - 21474836 - 2147483648 = 2147483647
Result: 21474836 < 2147483647 < 2147483648
Example 2:
Input: 30 10 20
Output: 10 < 20 < 30
Analysis:
• Values: a = 30, b = 10, c = 20
• Minimum: 10
• Maximum: 30
• Middle: 30 + 10 + 20 - 10 - 30 = 20
Result: Values sorted in ascending order
Example 3:
Input: 5 5 5
Output: 5 < 5 < 5
Analysis:
• All values are equal
• Minimum: 5, Maximum: 5
• Middle: 5 + 5 + 5 - 5 - 5 = 5
Result: Works correctly with equal values
Example 4 (Negative Numbers):
Input: -5 -15 -10
Output: -15 < -10 < -5
Analysis:
• Values: a = -5, b = -15, c = -10
• Minimum: -15 (most negative = smallest)
• Maximum: -5 (least negative = largest)
• Middle: -5 + (-15) + (-10) - (-15) - (-5) = -10
Result: -15 < -10 < -5
Example 5 (Already Sorted):
Input: 10 20 30
Output: 10 < 20 < 30
Analysis:
• Already in ascending order
• Algorithm still works correctly
Result: 10 < 20 < 30

⚠️ Constraints

Using only macros
-10^15 <= INPUT <= 10^15

✅ Solution

#include <iostream>
using namespace std;

// Macros to find maximum and minimum of two numbers
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))

int main() {
    long long a, b, c;
    cin >> a >> b >> c;

    // Find minimum, maximum, and middle using macros
    long long minimum = MIN(a, MIN(b, c));
    long long maximum = MAX(a, MAX(b, c));
    long long mid = a + b + c - minimum - maximum;

    cout << minimum << " < " << mid << " < " << maximum << endl;

    return 0;
}

🔍 Understanding Macro Definitions

What is a Macro?

A macro is a preprocessor directive that performs text substitution before the code is compiled. The #define directive creates a macro.

// Macro definition syntax:
#define MACRO_NAME(parameters) (replacement_text)

// Our MAX macro:
#define MAX(a,b) ((a)>(b)?(a):(b))
           ↓         ↓
        macro name  replacement

// When you write:
result = MAX(10, 20);

// Preprocessor replaces it with:
result = ((10)>(20)?(10):(20));
         // Evaluates to 20

Why Parentheses are Important:

// ❌ BAD: Without parentheses
#define MAX(a,b) a>b?a:b

// Problem with: MAX(x+1, y+1)
// Expands to: x+1>y+1?x+1:y+1
// Due to operator precedence, this behaves unexpectedly!

// ✅ GOOD: With parentheses
#define MAX(a,b) ((a)>(b)?(a):(b))

// Now: MAX(x+1, y+1)
// Expands to: ((x+1)>(y+1)?(x+1):(y+1))
// Works correctly regardless of what a and b are!

❓ Understanding the Ternary Operator

Syntax: condition ? value_if_true : value_if_false

// Ternary operator breakdown:
(a) > (b) ? (a) : (b)
 ↓     ↓    ↓    ↓
 |     |    |    └── Return b if condition is false
 |     |    └─────── Return a if condition is true
 |     └──────────── Comparison operator
 └────────────────── Operands being compared

// Example: MAX(10, 20)
((10)>(20) ? (10) : (20))
   ↓
  false (10 is not > 20)
   ↓
 returns 20 ✓

Ternary vs If-Else:

// Using if-else:
int max;
if (a > b)
    max = a;
else
    max = b;

// Using ternary (more concise):
int max = (a > b) ? a : b;

🧮 The Middle Value Trick

Formula: mid = a + b + c - minimum - maximum

Why This Works:

// Given three numbers: a, b, c
// Sum of all three: a + b + c
// If we subtract the smallest and largest:
// (a + b + c) - min - max = middle value

// Example: 10, 20, 30
mid = 10 + 20 + 30 - 10 - 30
    = 60 - 40
    = 20// Another example: 50, 10, 30
mid = 50 + 10 + 30 - 10 - 50
    = 90 - 60
    = 30// This is a mathematical property:
// Total sum minus extremes = middle value

Visual Representation:

     min          mid          max
      ↓            ↓            ↓
    [10]  ←───→  [20]  ←───→  [30]
      
    Sum = 10 + 20 + 30 = 60
    
    60 - 10 - 30 = 20 (middle value)

🔑 Key Concepts

  • Preprocessor Macros: Text substitution before compilation using #define
  • Ternary Operator: Compact conditional expression ? :
  • Nested Macros: Using macros within macro calls (like nested functions)
  • Parentheses in Macros: Essential to prevent operator precedence issues
  • Mathematical Trick: Finding middle value using sum subtraction
  • Generic Code: Macros work with any type (no type checking)

⚖️ Macros vs Functions

Aspect Macros (#define) Functions
Processing Time Compile-time (preprocessor) Runtime
Type Safety ❌ No type checking ✅ Type checked
Code Size Larger (code duplicated) Smaller (single copy)
Speed Faster (no call overhead) Slightly slower (call overhead)
Debugging Harder to debug Easier to debug
Side Effects ⚠️ Can have issues ✅ Safe

When to Use Each:

  • Use Macros: Simple operations, need to work with any type, performance-critical inline code
  • Use Functions: Complex logic, need type safety, easier debugging required
  • Modern C++: Prefer inline functions or constexpr over macros when possible

⚠️ Common Macro Pitfalls

1. Side Effects with Multiple Evaluation:

// ⚠️ WARNING: Arguments are evaluated multiple times!
#define MAX(a,b) ((a)>(b)?(a):(b))

int x = 5, y = 10;
int result = MAX(x++, y++);

// Expands to: ((x++)>(y++)?(x++):(y++))
// x++ is evaluated TWICE! (once in condition, once in result)
// This leads to unexpected behavior!

// ✅ SOLUTION: Don't use macros with expressions that have side effects

2. Operator Precedence Issues:

// ❌ BAD: No parentheses around parameters
#define SQUARE(x) x*x

int result = SQUARE(2+3);
// Expands to: 2+3*2+3 = 2+6+3 = 11 (WRONG!)

// ✅ GOOD: Parentheses around parameters
#define SQUARE(x) ((x)*(x))

int result = SQUARE(2+3);
// Expands to: ((2+3)*(2+3)) = 5*5 = 25 (CORRECT!)

3. No Namespace or Scope:

// Macros don't respect scope - they're global!
#define MAX(a,b) ((a)>(b)?(a):(b))

// This MAX will be replaced EVERYWHERE in the code
// Even inside string literals (in older compilers)

🚀 Modern C++ Alternative: Inline Functions

Modern C++ prefers inline or constexpr functions over macros:

// Modern approach: inline function (type-safe)
inline long long max_func(long long a, long long b) {
    return (a > b) ? a : b;
}

// C++14 and later: constexpr (compile-time when possible)
constexpr long long max_func(long long a, long long b) {
    return (a > b) ? a : b;
}

// C++14: Can use std::max directly
#include <algorithm>
auto result = std::max({a, b, c});

Why this is better:

  • ✅ Type safety
  • ✅ No multiple evaluation issues
  • ✅ Easier to debug
  • ✅ Better error messages
  • ✅ Can be used in namespaces

However, macros are still useful for:

  • Working with multiple types without templates
  • Conditional compilation (#ifdef)
  • Legacy code compatibility
  • Learning low-level C++ concepts

🌍 Real-world Applications

  • Legacy Code: Many older C/C++ libraries use macros extensively
  • Embedded Systems: Macros for hardware register manipulation
  • Game Development: Performance-critical inline code
  • Cross-platform Code: Conditional compilation with #ifdef
  • Assert Macros: Debug assertions (assert() is a macro)
  • Logging Systems: LOG_ERROR, LOG_DEBUG macros
  • Mathematical Libraries: Generic min/max operations

🎯 What You'll Learn

  • Understanding C++ preprocessor directives
  • Defining and using macros with #define
  • Mastering the ternary operator (? :)
  • Importance of parentheses in macro definitions
  • Mathematical tricks for finding middle values
  • Difference between compile-time and runtime operations
  • When to use macros vs functions
  • Common macro pitfalls and how to avoid them

💪 Extension Challenges

  • Level 1: Create a macro for absolute value: ABS(x)
  • Level 2: Create a macro for swapping: SWAP(a,b)
  • Level 3: Sort 3 numbers in descending order using macros
  • Level 4: Create CLAMP(x,min,max) macro (limit value to range)
  • Level 5: Sort 4 numbers using macros
  • Level 6: Compare macro vs inline function performance
  • Level 7: Create macros for common mathematical operations
  • Bonus: Implement the same program using inline functions and compare

💡 Practice Tips

  • Understand Expansion: Manually expand macros on paper to see how they work
  • Test Edge Cases: Equal values, negative numbers, very large numbers
  • Verify Middle Formula: Test a+b+c-min-max with different inputs
  • Experiment: Try writing macros without parentheses to see what breaks
  • Use Preprocessor Output: Compile with -E flag to see macro expansions
  • Compare Methods: Implement using functions and compare with macro version
  • Learn Ternary: Practice converting if-else to ternary operators