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;
}