FAC1003 — Programming Revision Questions — Answer Scheme

Full C++ solutions for FAC1003 — Programming Revision Questions (Functions, Recursion, Pointers).


Section A — Modular Functions

Q1 — Electricity Bill Calculator

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

const double FIXED_CHARGE = 5.00;
const double SURCHARGE_RATE = 0.05;
const double SURCHARGE_THRESHOLD = 600.0;
const double RATE_TIER1 = 0.218;    // First 200 kWh
const double RATE_TIER2 = 0.334;    // 201-300 kWh
const double RATE_TIER3 = 0.516;    // Above 300 kWh
const double TIER1_LIMIT = 200.0;
const double TIER2_LIMIT = 300.0;

// Non-void: calculates consumption charge based on tiered rates
double calculateCharge(double usage) {
    double charge = 0.0;

    if (usage <= TIER1_LIMIT) {
        charge = usage * RATE_TIER1;
    } else if (usage <= TIER2_LIMIT) {
        charge = TIER1_LIMIT * RATE_TIER1
               + (usage - TIER1_LIMIT) * RATE_TIER2;
    } else {
        charge = TIER1_LIMIT * RATE_TIER1
               + (TIER2_LIMIT - TIER1_LIMIT) * RATE_TIER2
               + (usage - TIER2_LIMIT) * RATE_TIER3;
    }

    return charge;
}

// Non-void: returns surcharge (5% of charge) if usage > threshold
double calculateSurcharge(double charge, double usage) {
    if (usage > SURCHARGE_THRESHOLD)
        return charge * SURCHARGE_RATE;
    else
        return 0.0;
}

// Void: displays full bill breakdown
void displayBill(string name, double usage, double charge,
                 double surcharge) {
    double total = charge + FIXED_CHARGE + surcharge;

    cout << "\n--- Electricity Bill ---" << endl;
    cout << "Customer Name: " << name << endl;
    cout << fixed << setprecision(2);
    cout << "Usage: " << usage << " kWh" << endl;
    cout << "Consumption Charge: RM" << charge << endl;
    cout << "Fixed Charge: RM" << FIXED_CHARGE << endl;

    if (surcharge > 0)
        cout << "Surcharge: RM" << surcharge
             << " (5% of consumption charge)" << endl;
    else
        cout << "Surcharge: RM0.00 (usage <= 600 kWh)" << endl;

    cout << "Total Amount: RM" << total << endl;
}

int main() {
    string name;
    double usage;

    cout << "Enter customer name: ";
    getline(cin, name);

    cout << "Enter total electricity usage (kWh): ";
    cin >> usage;

    double charge = calculateCharge(usage);
    double surcharge = calculateSurcharge(charge, usage);

    displayBill(name, usage, charge, surcharge);

    return 0;
}

Q2 — Loan Repayment Calculator

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

const double MIN_LOAN = 1000.0;
const double MAX_LOAN = 50000.0;
const double RATE_LOW = 0.045;      // <= 10000
const double RATE_HIGH = 0.039;     // > 10000
const double RATE_THRESHOLD = 10000.0;
const int VALID_TENURES[] = {6, 12, 24, 36};
const int NUM_TENURES = 4;

// Non-void: returns annual interest rate based on loan amount
double getInterestRate(double amount) {
    if (amount < MIN_LOAN || amount > MAX_LOAN)
        return -1.0;

    if (amount <= RATE_THRESHOLD)
        return RATE_LOW;
    else
        return RATE_HIGH;
}

// Non-void: calculates and returns monthly payment
double calculateMonthlyPayment(double amount, double rate,
                                int tenure) {
    double totalInterest = amount * rate * (tenure / 12.0);
    return (amount + totalInterest) / tenure;
}

// Void: displays full loan summary table
void displayLoanSummary(string name, double amount, int tenure,
                        double rate, double monthly) {
    double totalInterest = amount * rate * (tenure / 12.0);
    double totalRepayment = amount + totalInterest;

    cout << "\n--- Loan Summary ---" << endl;
    cout << "Customer: " << name << endl;
    cout << fixed << setprecision(2);
    cout << "Loan Amount: RM" << amount << endl;
    cout << "Annual Interest Rate: " << rate * 100 << "%" << endl;
    cout << "Tenure: " << tenure << " months" << endl;
    cout << "Total Interest: RM" << totalInterest << endl;
    cout << "Total Repayment: RM" << totalRepayment << endl;
    cout << "Monthly Payment: RM" << monthly << endl;
}

// Helper: checks if tenure is valid
bool isValidTenure(int tenure) {
    for (int i = 0; i < NUM_TENURES; i++) {
        if (VALID_TENURES[i] == tenure)
            return true;
    }
    return false;
}

int main() {
    string name;
    double amount;
    int tenure;

    cout << "Enter customer name: ";
    getline(cin, name);

    cout << "Enter loan amount (RM): ";
    cin >> amount;

    double rate = getInterestRate(amount);
    if (rate < 0) {
        cout << "Invalid loan amount. Must be between RM"
             << MIN_LOAN << " and RM" << MAX_LOAN << "." << endl;
        return 1;
    }

    do {
        cout << "Enter loan tenure (months): ";
        cin >> tenure;
        if (!isValidTenure(tenure)) {
            cout << "Invalid tenure. Must be 6, 12, 24, or 36 months."
                 << endl;
        }
    } while (!isValidTenure(tenure));

    double monthly = calculateMonthlyPayment(amount, rate, tenure);
    displayLoanSummary(name, amount, tenure, rate, monthly);

    return 0;
}

Section B — Recursion

Q3 — Digit Sum & Digital Root

#include <iostream>
using namespace std;

// Recursive: returns sum of all digits of n
int sumOfDigits(int n) {
    if (n == 0)
        return 0;
    return (n % 10) + sumOfDigits(n / 10);
}

// Recursive: returns digital root (single digit)
int digitalRoot(int n) {
    if (n < 10)
        return n;
    return digitalRoot(sumOfDigits(n));
}

// Helper: prints digit sum breakdown (e.g. "4 + 7 + 2 + 3")
void printDigitBreakdown(int n) {
    if (n < 10) {
        cout << n;
    } else {
        printDigitBreakdown(n / 10);
        cout << " + " << (n % 10);
    }
}

// Void: displays complete digit analysis
void displayDigitAnalysis(int original) {
    int sum = sumOfDigits(original);
    int root = digitalRoot(original);

    cout << "\n--- Digit Analysis ---" << endl;
    cout << "Number: " << original << endl;

    // Digit sum step-by-step
    cout << "Digit Sum: ";
    printDigitBreakdown(original);
    cout << " = " << sum << endl;

    // Digital root step-by-step
    cout << "Digital Root: ";
    if (original < 10) {
        cout << original << " (already a single digit)" << endl;
    } else {
        int temp = sum;
        while (temp >= 10) {
            cout << temp << " → ";
            temp = sumOfDigits(temp);
        }
        cout << temp << endl;
    }

    cout << "Final Digital Root: " << root << endl;
}

int main() {
    int num;

    cout << "Enter a positive integer: ";
    cin >> num;

    displayDigitAnalysis(num);

    return 0;
}

Q4 — Recursive Prime Factorisation

#include <iostream>
#include <cmath>
using namespace std;

// Recursive: checks if n is prime
// divisor starts at 2, increments recursively
bool isPrime(int n, int divisor = 2) {
    // Base cases
    if (n <= 2)
        return n == 2;
    if (n % divisor == 0)
        return false;
    // Stop when divisor^2 > n (no need to check further)
    if (divisor * divisor > n)
        return true;
    // Recursive: check next divisor
    return isPrime(n, divisor + 1);
}

// Recursive: prints prime factorisation
void factorise(int n, bool first = true) {
    if (n == 1)
        return;

    if (isPrime(n)) {
        if (!first)
            cout << " × ";
        cout << n;
        return;
    }

    // Find smallest divisor
    for (int d = 2; d * d <= n; d++) {
        if (n % d == 0) {
            if (!first)
                cout << " × ";
            else
                first = false;
            cout << d;
            factorise(n / d, false);
            return;
        }
    }
}

// Void: displays the full factorisation
void displayFactorisation(int n) {
    cout << n << " = ";
    if (isPrime(n)) {
        cout << n << " (prime)" << endl;
    } else {
        factorise(n, true);
        cout << endl;
    }
}

int main() {
    int n;

    do {
        cout << "Enter a positive integer (> 1): ";
        cin >> n;
        if (n <= 1)
            cout << "Please enter a number greater than 1." << endl;
    } while (n <= 1);

    displayFactorisation(n);

    return 0;
}

Section C — Pointers & Dynamic 2D Arrays

Q5 — Temperature Anomaly Tracker

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

// Void: computes and displays stats for one station
void displayStationReport(double* anomalies, int numReadings,
                          int stationIndex) {
    double sum = 0.0;
    double maxVal = *(anomalies + 0);  // first element via pointer
    double minVal = *(anomalies + 0);

    for (int j = 0; j < numReadings; j++) {
        double val = *(anomalies + j);
        sum += val;
        if (val > maxVal) maxVal = val;
        if (val < minVal) minVal = val;
    }

    double mean = sum / numReadings;
    double range = maxVal - minVal;

    // Classification
    string classification;
    if (mean > 2.0)
        classification = "Extreme Warming";
    else if (mean >= 0.5)
        classification = "Moderate Warming";
    else if (mean >= -0.5)
        classification = "Stable";
    else if (mean >= -2.0)
        classification = "Moderate Cooling";
    else
        classification = "Extreme Cooling";

    cout << "Station " << stationIndex + 1
         << ": Mean = " << fixed << setprecision(2) << mean
         << "°C | Max = " << maxVal
         << "°C | Min = " << minVal
         << "°C | Range = " << range
         << "°C | " << classification << endl;
}

int main() {
    int numStations, numReadings;

    cout << "Enter number of weather stations: ";
    cin >> numStations;
    cout << "Enter number of daily readings: ";
    cin >> numReadings;

    // Dynamically allocate 2D array using pointer-to-pointer
    double** data = new double*[numStations];
    for (int i = 0; i < numStations; i++)
        *(data + i) = new double[numReadings];

    // Read data — pointer notation only
    for (int i = 0; i < numStations; i++) {
        for (int j = 0; j < numReadings; j++) {
            cout << "Station " << i + 1
                 << ", Reading " << j + 1 << ": ";
            cin >> *(*(data + i) + j);
        }
    }

    // Process and display each station
    cout << "\n--- Station Report ---" << endl;
    for (int i = 0; i < numStations; i++) {
        displayStationReport(*(data + i), numReadings, i);
    }

    // Memory cleanup
    for (int i = 0; i < numStations; i++)
        delete[] *(data + i);
    delete[] data;

    return 0;
}

Q6 — Sparse Matrix Compression

#include <iostream>
#include <iomanip>
using namespace std;

// Void: compresses a matrix into sparse representation
void compressMatrix(int** matrix, int rows, int cols,
                    int*& values, int*& rowIndices,
                    int*& colIndices, int& nonZeroCount) {
    // First pass: count non-zero elements
    nonZeroCount = 0;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (*(*(matrix + i) + j) != 0)
                nonZeroCount++;
        }
    }

    // Allocate the three arrays
    values = new int[nonZeroCount];
    rowIndices = new int[nonZeroCount];
    colIndices = new int[nonZeroCount];

    // Second pass: fill the arrays
    int idx = 0;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            int val = *(*(matrix + i) + j);
            if (val != 0) {
                *(values + idx) = val;
                *(rowIndices + idx) = i;
                *(colIndices + idx) = j;
                idx++;
            }
        }
    }
}

// Void: displays the full matrix in grid format
void displayMatrix(int** matrix, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            cout << setw(3) << *(*(matrix + i) + j);
        }
        cout << endl;
    }
}

// Void: displays compressed representation as a table
void displayCompressed(int* values, int* rowIndices,
                       int* colIndices, int nonZeroCount) {
    cout << "\nCompressed Representation (Non-zero elements: "
         << nonZeroCount << "):" << endl;
    cout << "Value   Row   Col" << endl;
    for (int k = 0; k < nonZeroCount; k++) {
        cout << setw(3) << *(values + k)
             << "    " << setw(2) << *(rowIndices + k)
             << "    " << setw(2) << *(colIndices + k) << endl;
    }
}

// Void: reconstructs matrix from compressed form and displays it
void reconstructAndDisplay(int* values, int* rowIndices,
                           int* colIndices, int nonZeroCount,
                           int rows, int cols) {
    // Dynamically allocate reconstructed matrix
    int** reconstructed = new int*[rows];
    for (int i = 0; i < rows; i++)
        *(reconstructed + i) = new int[cols];

    // Initialise all to zero
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            *(*(reconstructed + i) + j) = 0;

    // Place non-zero values from compressed representation
    for (int k = 0; k < nonZeroCount; k++) {
        int r = *(rowIndices + k);
        int c = *(colIndices + k);
        *(*(reconstructed + r) + c) = *(values + k);
    }

    cout << "\nReconstructed Matrix:" << endl;
    displayMatrix(reconstructed, rows, cols);

    // Cleanup
    for (int i = 0; i < rows; i++)
        delete[] *(reconstructed + i);
    delete[] reconstructed;
}

int main() {
    int rows, cols;

    cout << "Enter number of rows: ";
    cin >> rows;
    cout << "Enter number of columns: ";
    cin >> cols;

    // Dynamically allocate original matrix
    int** matrix = new int*[rows];
    for (int i = 0; i < rows; i++)
        *(matrix + i) = new int[cols];

    // Read matrix elements
    for (int i = 0; i < rows; i++) {
        cout << "\nRow " << i << ":" << endl;
        for (int j = 0; j < cols; j++) {
            cout << "  Col " << j << ": ";
            cin >> *(*(matrix + i) + j);
        }
    }

    // Display original matrix
    cout << "\nOriginal Matrix:" << endl;
    displayMatrix(matrix, rows, cols);

    // Compress
    int* values = nullptr;
    int* rowIndices = nullptr;
    int* colIndices = nullptr;
    int nonZeroCount = 0;

    compressMatrix(matrix, rows, cols,
                   values, rowIndices, colIndices,
                   nonZeroCount);

    if (nonZeroCount == 0) {
        cout << "\nNo non-zero elements found. Matrix is entirely zero."
             << endl;
    } else {
        displayCompressed(values, rowIndices, colIndices,
                          nonZeroCount);
        reconstructAndDisplay(values, rowIndices, colIndices,
                              nonZeroCount, rows, cols);
        cout << "\nMatrices match!" << endl;
    }

    // Cleanup all memory
    delete[] values;
    delete[] rowIndices;
    delete[] colIndices;
    for (int i = 0; i < rows; i++)
        delete[] *(matrix + i);
    delete[] matrix;

    return 0;
}

Related