Cambridge IAL · thinka-original Practice Paper

2026 Cambridge IAL Computer Science (9618) Practice Paper with Answers

Thinka Jun 2026 (V3) Cambridge International A Level-Style Mock — Computer Science (9618)

300 marks450 mins2026
An original Thinka practice paper modelled on the structure and difficulty of the Jun 2026 (V3) Cambridge International A Level Computer Science (9618) paper. Not affiliated with or reproduced from Cambridge.

Paper 1 Theory Fundamentals

Answer all questions. Calculators must not be used.
10 Question · 64 marks
Question 1 · Short Answer & Fill-in-the-blank
4.5 marks
Complete the following sentences regarding digital sound representation by filling in each blank with the correct technical term or value.

(a) The number of audio snapshots taken per unit time is known as the ....................................................., which is typically measured in ..................................................... .

(b) The number of bits allocated to represent the amplitude of each snapshot is the ..................................................... .

(c) An uncompressed stereo sound recording is sampled for \(10\) seconds at a frequency of \(44.1\text{ kHz}\) with a bit depth of \(16\text{ bits}\). Calculate the total file size in mebibytes (MiB), showing your working. (You may leave your final answer as an unsimplified mathematical expression or calculate it to 2 decimal places).
Show answer & marking scheme

Worked solution

(a) Sampling rate (or sampling frequency) measures how frequently samples are taken; its standard unit is Hertz (Hz) or kilohertz (kHz).
(b) Sampling resolution (also referred to as bit depth) defines the precision/amplitude range encoded for each sample.
(c) Calculation:
• Total samples per channel = 44,100 samples/sec × 10 sec = 441,000 samples
• Total bits for 2 channels (stereo) = 441,000 × 16 bits × 2 = 14,112,000 bits
• Conversion to bytes = 14,112,000 / 8 = 1,764,000 bytes
• Conversion to mebibytes (MiB) = 1,764,000 / (1024 × 1024) = 1,764,000 / 1,048,576 ≈ 1.682 MiB (or 1.68 MiB).

Marking scheme

1 mark each for the following points (up to 4.5 marks max):
• MP1 (0.5 mark): sampling rate / sampling frequency
• MP2 (0.5 mark): Hertz / Hz / kHz
• MP3 (1 mark): sampling resolution / bit depth / sample size
• MP4 (1 mark): Multiplying sample rate × duration × bit depth × 2 (channels) [44100 × 10 × 16 × 2]
• MP5 (1.5 marks): Correct division by (8 × 1024 × 1024) or 8,388,608 to convert bits to MiB, yielding 1.68 MiB (or exact unsimplified fraction 14,112,000 / 8,388,608). Award 1 mark if divided by 10^6 (decimal megabyte slip) with clear method shown.
Question 2 · Short Answer & Fill-in-the-blank
4.5 marks
The von Neumann architecture uses several dedicated registers during the Fetch-Execute cycle.

(a) Identify the register that stores the address of the next instruction to be fetched from memory: ..................................................... .

(b) Describe the role of the Memory Data Register (MDR) during the fetch stage.

(c) Complete the following register transfer notation (RTN) representing the initial fetch sequence:
1. MAR <- [ .................... ]
2. PC <- [ PC ] + 1
3. MDR <- [ [ .................... ] ]
4. CIR <- [ MDR ]
Show answer & marking scheme

Worked solution

(a) The Program Counter (PC) holds the memory address of the next instruction waiting to be fetched.
(b) During the fetch stage, the MDR temporarily holds the contents (instruction or data word) retrieved from the RAM location indicated by the MAR via the data bus before it is copied to the CIR.
(c) In RTN:
Step 1: MAR <- [ PC ] transfers the address in PC to MAR.
Step 3: MDR <- [ [ MAR ] ] loads the content of the memory address referenced by MAR into the MDR.

Marking scheme

Mark breakdown (total 4.5 marks):
• (a) [1 mark]: Program Counter / PC.
• (b) [2 marks]: 1 mark for stating it temporarily holds the data/instruction fetched from memory; 1 mark for stating it receives the data via the data bus / from the location addressed by MAR.
• (c) [1.5 marks]: 0.75 mark for PC in step 1; 0.75 mark for MAR in step 3.
Question 3 · Short Answer & Fill-in-the-blank
4.5 marks
A logistics company uses a relational database to store cargo records.

(a) Define what is meant by a foreign key in a relational database.

(b) State the database concept that is violated if a foreign key value refers to a non-existent primary key in the related table: ..................................................... .

(c) The table `SHIPMENT` has attributes `ShipmentID`, `DestinationPort`, `WeightKg`, and `FeeUSD`.
Write an SQL statement to display the `DestinationPort` and the average `WeightKg` (named as `AvgWeight`) for all shipments, grouped by `DestinationPort`, where `FeeUSD` is greater than \(500\).
Show answer & marking scheme

Worked solution

(a) A foreign key is a field in a relational table that matches the primary key column of another table, providing a logical link and enforcing referential integrity between tables.
(b) Referential integrity.
(c) SQL solution:
SELECT DestinationPort, AVG(WeightKg) AS AvgWeight
FROM SHIPMENT
WHERE FeeUSD > 500
GROUP BY DestinationPort;

Marking scheme

Mark breakdown (total 4.5 marks):
• (a) [1.5 marks]: 1 mark for attribute in one table that references the primary key of another table; 0.5 mark for linking/creating a relationship between records.
• (b) [1 mark]: Referential integrity (Do not accept just 'integrity' or 'validation').
• (c) [2 marks]:
- 0.5 mark for correct SELECT DestinationPort, AVG(WeightKg) [accept with or without AS AvgWeight]
- 0.5 mark for correct FROM SHIPMENT
- 0.5 mark for correct WHERE FeeUSD > 500
- 0.5 mark for correct GROUP BY DestinationPort
Question 4 · Short Answer & Fill-in-the-blank
4.5 marks
Networks use protocols, addressing schemes, and topologies to transmit data reliably.

(a) State two differences between an IPv4 address and an IPv6 address.

(b) Fill in the blanks with the appropriate terms describing network transmission:
When multiple nodes on an Ethernet network attempt to transmit data simultaneously, a ..................................................... occurs. The protocol used to detect this and manage retransmission is ..................................................... .

(c) Identify the networking device that inspects data packets at the network layer and determines the optimal path to forward them to their destination network: ..................................................... .
Show answer & marking scheme

Worked solution

(a) Key differences:
1. Bit length: IPv4 uses 32 bits (allowing approx. 4.3 billion addresses), while IPv6 uses 128 bits.
2. Notation format: IPv4 is written as 4 denary numbers separated by dots (e.g., 192.168.1.1), while IPv6 is written in 8 groups of 4 hexadecimal digits separated by colons.
(b) A data collision occurs when transmissions overlap. Ethernet uses CSMA/CD (Carrier Sense Multiple Access with Collision Detection) to manage access and collisions.
(c) A router is the layer 3 device responsible for routing packets based on IP addresses.

Marking scheme

Mark breakdown (total 4.5 marks):
• (a) [2 marks]: 1 mark for each valid difference (e.g., 32-bit vs 128-bit; dotted-decimal vs colon-hexadecimal; presence of built-in security IPSec in IPv6 / larger address space).
• (b) [1.5 marks]: 0.75 mark for 'collision' (or 'packet collision'); 0.75 mark for 'CSMA/CD' (Carrier Sense Multiple Access with Collision Detection).
• (c) [1 mark]: Router.
Question 5 · Calculations & Data Conversions
5 marks
(a) A digital graphic designer creates a bitmap image that has a width of 1024 pixels and a height of 512 pixels. The image is configured to use a palette of 256 distinct colours.

(i) State the minimum colour depth, in bits per pixel, required to represent this image.

(ii) Calculate the minimum file size of the raw image data in kibibytes (KiB), excluding any file headers. Show all your working.

(b) The designer alters the image settings to use a 24-bit true colour depth while keeping the image dimensions unchanged.

Calculate the new file size of the image data in mebibytes (MiB). Show all your working.
Show answer & marking scheme

Worked solution

(a)(i)
Number of colours = 256
Colour depth = \(\log_2(256) = 8\) bits per pixel.

(a)(ii)
Total number of pixels = \(1024 \times 512 = 524,288\) pixels
Size in bits = \(1024 \times 512 \times 8\) bits
Size in bytes = \(\frac{1024 \times 512 \times 8}{8} = 1024 \times 512 = 524,288\) bytes
Size in KiB = \(\frac{524,288}{1024} = 512\) KiB

(b)
New colour depth = 24 bits = 3 bytes per pixel
Total bytes = \(1024 \times 512 \times 3\) bytes
Size in MiB = \(\frac{1024 \times 512 \times 3}{1024 \times 1024} = \frac{512 \times 3}{1024} = \frac{1536}{1024} = 1.5\) MiB

Marking scheme

(a)(i) [1 mark]
- 8 (bits / bits per pixel / 1 byte) [1]

(a)(ii) [2 marks]
- 1 mark for showing valid working (e.g. \((1024 \times 512 \times 8) / (8 \times 1024)\) or \(524,288 / 1024\)) [1]
- 1 mark for correct final answer: 512 (KiB) [1]

(b) [2 marks]
- 1 mark for showing division by \(1024 \times 1024\) (or \(1,048,576\)) with 24 bits / 3 bytes [1]
- 1 mark for correct final answer: 1.5 (MiB) [1]

Guidance notes:
- Units given in prompt; award mark for numerical value if units omitted.
- Allow ECF in (a)(ii) from incorrect colour depth in (a)(i).
Question 6 · Calculations & Data Conversions
5 marks
Two numbers are stored in an 8-bit register using two's complement integer representation:

Number A: 10010110
Number B: 10101000

(a) Convert Number A into denary. Show your working.

(b) Perform the binary addition of Number A and Number B using 8-bit two's complement arithmetic. Show all your working, including carries.

(c) State whether arithmetic overflow has occurred in the addition in part (b). Justify your answer.
Show answer & marking scheme

Worked solution

(a)
Method 1 (place values):
-128 + 16 + 4 + 2 = -106
Method 2 (inversion):
Invert bits: 01101001
Add 1: 01101010 = 64 + 32 + 8 + 2 = 106
Apply negative sign = -106

(b)
Carry row: 1 0 0 1 0 0 0 0
10010110
+ 10101000
----------
(1)00111110
Discarding the 9th bit yields 00111110.

(c)
Overflow occurred.
Justification: Both operands are negative (MSB = 1), but the result is positive (MSB = 0) / the calculated denary total is -194, which exceeds the minimum representable limit of -128 for an 8-bit two's complement integer.

Marking scheme

(a) [2 marks]
- 1 mark for showing valid working (e.g. \(-128 + 16 + 4 + 2\) or \(-(01101001 + 1)\)) [1]
- 1 mark for correct denary value: -106 [1]

(b) [2 marks]
- 1 mark for evidence of binary addition with carries shown [1]
- 1 mark for correct 8-bit result: 00111110 [1]

(c) [1 mark]
- 1 mark for stating that overflow occurred AND providing a valid reason (e.g. adding two negative numbers produces a positive result / true sum -194 cannot be represented in 8 bits / result is outside range -128 to 127 / carry into sign bit differs from carry out). [1]
Question 7 · database_design_and_sql
8.5 marks
A veterinary surgery uses a relational database to manage appointments and records of registered animals.

The database includes the following two tables:

OWNER (OwnerID, FirstName, LastName, PhoneNumber, RegistrationDate)
PATIENT (PatientID, PatientName, Species, DateOfBirth, MicrochipNumber, OwnerID)

(a) Explain what is meant by referential integrity in the context of this relational database. [2]

(b) Write an SQL Data Definition Language (DDL) script to create the table `PATIENT`.

Your script must:
- define appropriate data types for each attribute
- specify the primary key
- specify the foreign key referencing the `OWNER` table. [3.5]

(c) The surgery requires a list showing the total number of registered patients for each species, but only for species with more than 3 registered animals.

Write an SQL Data Manipulation Language (DML) query to display the `Species` and the number of patients (aliased as `TotalCount`) for these species, ordered in descending order of the count. [3]
Show answer & marking scheme

Worked solution

(a)
- Referential integrity is a property of relational databases stating that every foreign key value in a table (e.g. `OwnerID` in `PATIENT`) must match a valid, existing primary key value in the corresponding parent table (`OWNER`).
- This ensures consistency and prevents records in the child table from becoming orphaned (e.g., an animal registered to a non-existent owner).

(b)
```sql
CREATE TABLE PATIENT (
PatientID VARCHAR(8) NOT NULL,
PatientName VARCHAR(30),
Species VARCHAR(20),
DateOfBirth DATE,
MicrochipNumber VARCHAR(15),
OwnerID VARCHAR(8),
PRIMARY KEY (PatientID),
FOREIGN KEY (OwnerID) REFERENCES OWNER(OwnerID)
);
```

(c)
```sql
SELECT Species, COUNT(PatientID) AS TotalCount
FROM PATIENT
GROUP BY Species
HAVING COUNT(PatientID) > 3
ORDER BY COUNT(PatientID) DESC;
```
(Note: `ORDER BY TotalCount DESC` is also valid).

Marking scheme

(a) [Max 2 marks]
- MP1: 1 mark for stating that a foreign key value must correspond to an existing primary key in the referenced parent table (or be null).
- MP2: 1 mark for explaining the purpose / effect: prevents orphaned records / ensures relationships remain valid and data is consistent.

(b) [3.5 marks total]
- MP1 (1 mark): `CREATE TABLE PATIENT (...)` with all 6 attributes included and valid data types (e.g., `VARCHAR` / `CHARACTER` for IDs and names, `DATE` for `DateOfBirth`).
- MP2 (1 mark): Correct declaration of primary key: `PRIMARY KEY (PatientID)`.
- MP3 (1.5 marks): Correct declaration of foreign key: `FOREIGN KEY (OwnerID) REFERENCES OWNER(OwnerID)` (1 mark for syntax `FOREIGN KEY ... REFERENCES ...`, 0.5 mark for correct table and field names).

(c) [3 marks total]
- MP1 (1 mark): `SELECT Species, COUNT(...) [AS TotalCount] FROM PATIENT`.
- MP2 (1 mark): `GROUP BY Species` and `HAVING COUNT(...) > 3`.
- MP3 (1 mark): `ORDER BY COUNT(...) DESC` (or `ORDER BY TotalCount DESC`).
Question 8 · database_design_and_sql
8.5 marks
A parcel delivery company operates a regional depot. Information regarding vehicles and delivery routes is stored in a relational database.

The database schema is defined as follows:

VEHICLE (RegistrationNo, Model, PayloadKg, LastServiceDate)
DELIVERY_RUN (RunID, RunDate, RouteCode, DriverName, RegistrationNo)

(a) Define the term secondary key and state a suitable secondary key for the `DELIVERY_RUN` table. [2]

(b) Write an SQL Data Definition Language (DDL) command to add a new attribute named `Status` to the `VEHICLE` table. The attribute must store a variable-length string up to 15 characters. [1.5]

(c) The operations manager needs a report showing all delivery runs scheduled on or after 1st December 2026 ('2026-12-01') that use a vehicle with a `PayloadKg` greater than 1200.

Write an SQL Data Manipulation Language (DML) query to display the `RunID`, `RunDate`, `DriverName`, `Model`, and `PayloadKg` for these runs. Results must be sorted in ascending order of `RunDate`. [5]
Show answer & marking scheme

Worked solution

(a)
- Definition: A secondary key is an attribute (or group of attributes) used as an alternate index to facilitate efficient searching, retrieval, or sorting of records, but is not the primary key.
- Suitable attribute: `DriverName` / `RouteCode` / `RunDate`.

(b)
```sql
ALTER TABLE VEHICLE
ADD Status VARCHAR(15);
```

(c)
```sql
SELECT DELIVERY_RUN.RunID, DELIVERY_RUN.RunDate, DELIVERY_RUN.DriverName, VEHICLE.Model, VEHICLE.PayloadKg
FROM DELIVERY_RUN
INNER JOIN VEHICLE ON DELIVERY_RUN.RegistrationNo = VEHICLE.RegistrationNo
WHERE DELIVERY_RUN.RunDate >= '2026-12-01' AND VEHICLE.PayloadKg > 1200
ORDER BY DELIVERY_RUN.RunDate ASC;
```
(Note: Table qualifiers are optional where field names are unambiguous, but recommended for clarity).

Marking scheme

(a) [2 marks]
- MP1 (1 mark): Explanation that a secondary key is an indexed attribute/field used to search/locate records quickly (an alternative index).
- MP2 (1 mark): Identification of a valid candidate attribute from `DELIVERY_RUN` (e.g. `DriverName`, `RouteCode`, or `RunDate`).

(b) [1.5 marks]
- MP1 (0.5 mark): `ALTER TABLE VEHICLE`.
- MP2 (1 mark): `ADD Status VARCHAR(15)` (Allow `CHARACTER(15)` or `ADD COLUMN`).

(c) [5 marks]
- MP1 (1 mark): `SELECT RunID, RunDate, DriverName, Model, PayloadKg` (all 5 required attributes present).
- MP2 (1 mark): `FROM DELIVERY_RUN INNER JOIN VEHICLE` (or `FROM DELIVERY_RUN, VEHICLE`).
- MP3 (1 mark): Join condition: `ON DELIVERY_RUN.RegistrationNo = VEHICLE.RegistrationNo` (or in `WHERE` clause).
- MP4 (1 mark): Filter condition: `WHERE RunDate >= '2026-12-01' AND PayloadKg > 1200`.
- MP5 (1 mark): `ORDER BY RunDate [ASC]`.
Question 9 · Assembly & Hardware Descriptions
9.5 marks
(a) Describe the difference between direct addressing and indirect addressing in assembly language. [2]

(b) An assembly language program is executed on a processor with an Accumulator (ACC) and an Index Register (IX). The following table shows a portion of memory containing code and data:

Address | Instruction / Data
--- | ---
100 | LDM #0
101 | STO 200
102 | LOOP: LDX 150
103 | CMP 201
104 | JPE EXIT
105 | LDD 200
106 | ADD 202
107 | STO 200
108 | INC IX
109 | JMP LOOP
110 | EXIT: END
...
150 | 12
151 | 18
152 | 25
153 | 99
...
200 | 0
201 | 25
202 | 4

Assume the Index Register (IX) initially contains 0. Trace the execution of the program until the instruction at address 110 (EXIT: END) is reached. Show each change in the values of IX, ACC, and memory location 200. [4.5]

(c) The Fetch-Execute cycle relies on dedicated processor registers and buses.
(i) State the principal function of the Current Instruction Register (CIR). [1]
(ii) Write the Fetch stage of the Fetch-Execute cycle using Register Transfer Notation (RTN). [2]
Show answer & marking scheme

Worked solution

(a)
- Direct addressing: The operand part of the instruction specifies the actual memory address where the data value to be operated on is stored.
- Indirect addressing: The operand specifies a memory address which contains the memory address where the data is stored (a pointer to the data).

(b) Trace of execution:
1. Initial: IX = 0, ACC = 0, Mem[200] = 0.
2. Iteration 1 (IX = 0):
- Address 102 (LDX 150): Effective address = 150 + 0 = 150. ACC <- Mem[150] = 12.
- Address 103 (CMP 201): Compares ACC (12) with Mem[201] (25) -> Not equal.
- Address 104 (JPE EXIT): Condition false, does not jump.
- Address 105 (LDD 200): ACC <- Mem[200] = 0.
- Address 106 (ADD 202): ACC <- 0 + Mem[202] (4) = 4.
- Address 107 (STO 200): Mem[200] <- 4.
- Address 108 (INC IX): IX <- 1.
- Address 109 (JMP LOOP): Jumps to 102.
3. Iteration 2 (IX = 1):
- Address 102 (LDX 150): Effective address = 150 + 1 = 151. ACC <- Mem[151] = 18.
- Address 103 (CMP 201): Compares ACC (18) with Mem[201] (25) -> Not equal.
- Address 104 (JPE EXIT): Condition false, does not jump.
- Address 105 (LDD 200): ACC <- Mem[200] = 4.
- Address 106 (ADD 202): ACC <- 4 + 4 = 8.
- Address 107 (STO 200): Mem[200] <- 8.
- Address 108 (INC IX): IX <- 2.
- Address 109 (JMP LOOP): Jumps to 102.
4. Iteration 3 (IX = 2):
- Address 102 (LDX 150): Effective address = 150 + 2 = 152. ACC <- Mem[152] = 25.
- Address 103 (CMP 201): Compares ACC (25) with Mem[201] (25) -> Equal.
- Address 104 (JPE EXIT): Jump condition met; branches to 110 (EXIT).
- Address 110: END.

Final values:
IX = 2
ACC = 25
Location 200 = 8

(c)(i)
CIR stores the binary instruction that is currently being decoded and executed by the Control Unit.

(c)(ii)
MAR <- [PC]
PC <- [PC] + 1
MDR <- [[MAR]]
CIR <- [MDR]

Marking scheme

(a) [2 marks]
- 1 mark for direct addressing definition: operand is the address of the data.
- 1 mark for indirect addressing definition: operand is the address of an address / pointer to data.

(b) [4.5 marks]
- 1.5 marks: Correct first iteration trace (IX becomes 1, ACC transitions 0 -> 12 -> 0 -> 4, Mem[200] becomes 4).
- 1.5 marks: Correct second iteration trace (IX becomes 2, ACC transitions to 18 -> 4 -> 8, Mem[200] becomes 8).
- 1.5 marks: Correct final iteration and termination (ACC loads 25, comparison causes branch on JPE EXIT, terminating with IX = 2, ACC = 25, Mem[200] = 8).

(c)(i) [1 mark]
- 1 mark: Holds the current instruction while it is being decoded / executed.

(c)(ii) [2 marks]
- 1 mark for MAR <- [PC] AND PC <- [PC] + 1
- 1 mark for MDR <- [[MAR]] AND CIR <- [MDR]
(Accept equivalent standard Register Transfer Notation formats. Brackets indicating contents of register are required).
Question 10 · Assembly & Hardware Descriptions
9.5 marks
(a) An automated chemical processing facility uses an embedded monitoring and control system to maintain safe operating conditions in a liquid storage vat.
(i) Explain the distinct roles of an Analogue-to-Digital Converter (ADC) and an actuator in this monitoring and control system. [2.5]
(ii) Explain the purpose of a hardware buffer when sensor readings are transferred to primary memory. [1.5]

(b) The embedded processor reads an 8-bit status byte from hardware input port &FE into the Accumulator (ACC). The status byte format is defined as follows:
- Bit 7: Cooler active (1 = active, 0 = inactive)
- Bit 6: Heater active (1 = active, 0 = inactive)
- Bit 5: High pressure alert (1 = alert, 0 = normal)
- Bit 4: Low level alert (1 = alert, 0 = normal)
- Bits 3 to 0: Unused (all set to 0)

(i) Write a single assembly language instruction using a bitwise logic operation and a binary operand to mask out all bits except Bit 5 and Bit 4. [1.5]
(ii) An initial reading from port &FE produces the byte B00010000 in the Accumulator. Complete the following assembly language routine to determine if the Low level alert (Bit 4) is triggered, and branch to the label LOW_ALERT if it is set. [2]

IN &FE
AND B00010000
...................
...................

(c) The assembly source code is translated into executable machine code by a two-pass assembler.
Describe the role of the symbol table during Pass 1 and Pass 2 of the two-pass assembly process. [2]
Show answer & marking scheme

Worked solution

(a)(i)
- Analogue-to-Digital Converter (ADC): Sensors measure continuous analogue physical properties (e.g. vat temperature, pressure, fluid level). The ADC converts these continuous analogue voltages into discrete digital values that the processor can read and process.
- Actuator: An output transducer/device that translates electrical/digital control signals from the computer system into physical motion or mechanical actions (e.g., turning on a heater, operating a cooling fan, or opening/closing a pressure-relief valve).

(a)(ii)
- A hardware buffer provides temporary storage to compensate for the difference in operational speed between the external hardware/sensors (which transfer data relatively slowly) and the processor/system bus (which operates at high speed), preventing the processor from being held in an idle wait state during data transfers.

(b)(i)
To mask all bits except Bit 5 and Bit 4, perform a bitwise AND with a mask where Bits 5 and 4 are 1 and all other bits are 0 (i.e., B00110000):
AND B00110000

(b)(ii)
After executing:
IN &FE
AND B00010000
If Bit 4 was 1, ACC contains B00010000 (#16). If Bit 4 was 0, ACC contains B00000000 (#0).
To jump to LOW_ALERT if set:
Option A:
CMI #0
JPN LOW_ALERT
(or CMI #16 / CMI B00010000 followed by JPE LOW_ALERT)

(c)
- Pass 1: The assembler scans the source code, determines the memory allocation for each instruction/data item, and builds the symbol table by recording each label along with its assigned memory address / offset.
- Pass 2: The assembler uses the completed symbol table to resolve symbolic references, replacing each label/symbol with its actual binary/hexadecimal address when generating machine code.

Marking scheme

(a)(i) [2.5 marks]
- 1 mark for ADC role: converts analogue input signals from sensors into digital values suitable for processing by the CPU.
- 1.5 marks for Actuator role: physical output mechanism that converts digital/electrical control commands into physical action / movement / operation of hardware components (e.g. valves/heaters).

(a)(ii) [1.5 marks]
- 1.5 marks for explaining buffer purpose: temporary storage area allowing data to be held to compensate for speed mismatches between slow sensor/input hardware and fast CPU/memory bus.

(b)(i) [1.5 marks]
- 1.5 marks for correct instruction and operand: AND B00110000 (Allow AND &30 or AND #48).

(b)(ii) [2 marks]
- 1 mark for valid comparison instruction (e.g., CMI #0 or CMI #16 or CMI B00010000).
- 1 mark for corresponding conditional jump instruction (e.g., JPN LOW_ALERT when compared with 0, or JPE LOW_ALERT when compared with #16/B00010000).

(c) [2 marks]
- 1 mark for Pass 1: generates the symbol table by storing label names and calculating/assigning their absolute/relative memory addresses.
- 1 mark for Pass 2: looks up addresses from the symbol table to replace symbolic operands/labels with actual memory addresses in the final machine code.

Ready to test yourself?

Turn these notes into exam-style practice. Get unlimited AI questions on this topic with instant marking and explanations.

Practise This Topic

Paper 2 Fundamental Problem-solving and Programming Skills

Answer all questions. Use the provided pseudocode insert.
8 Question · 75 marks
Question 1 · short-answer
4 marks
The built-in pseudocode string manipulation and mathematical functions are defined as follows:
- `MID(ThisString : STRING, Start : INTEGER, Length : INTEGER) RETURNS STRING`
- `LENGTH(ThisString : STRING) RETURNS INTEGER`
- `INT(ThisReal : REAL) RETURNS INTEGER`
- `DIV(n1 : INTEGER, n2 : INTEGER) RETURNS INTEGER`
- `MOD(n1 : INTEGER, n2 : INTEGER) RETURNS INTEGER`

The following variables are declared and assigned initial values:
```
DECLARE CodeString : STRING
DECLARE NumA : INTEGER
DECLARE NumB : REAL

CodeString <- "COMPUTING9618"
NumA <- 19
NumB <- 4.75
```

Evaluate each of the following expressions:

(a) `LENGTH(CodeString) - 5`

(b) `MID(CodeString, 4, 3)`

(c) `NumA MOD 4 + INT(NumB)`

(d) `(NumA DIV 5) >= 4 AND NumB > 4.0`
Show answer & marking scheme

Worked solution

(a) LENGTH("COMPUTING9618") returns 13. Evaluating 13 - 5 gives 8.
(b) MID("COMPUTING9618", 4, 3) extracts 3 characters starting at index 4 ('P', 'U', 'T'), returning "PUT".
(c) NumA MOD 4 gives 19 MOD 4 = 3. INT(NumB) gives INT(4.75) = 4. 3 + 4 gives 7.
(d) NumA DIV 5 gives 19 DIV 5 = 3. (3 >= 4) evaluates to FALSE. NumB > 4.0 gives (4.75 > 4.0) which is TRUE. FALSE AND TRUE evaluates to FALSE.

Marking scheme

1 mark per correct answer:
(a) 8 [1]
(b) "PUT" (accept PUT without quotes) [1]
(c) 7 [1]
(d) FALSE (do not accept False, F, or 0) [1]
Question 2 · short-answer
4 marks
A programmer writes the following pseudocode algorithm to process readings from a temperature monitor:

```
CONSTANT SENSOR_LIMIT = 85.0
DECLARE ReadingCount : INTEGER
DECLARE TotalValue : REAL
DECLARE IsActive : BOOLEAN
DECLARE StatusMessage : STRING

ReadingCount <- 0
TotalValue <- 0.0
IsActive <- TRUE

REPEAT
INPUT CurrentReading
IF CurrentReading > SENSOR_LIMIT THEN
StatusMessage <- "ALERT: Overheat"
IsActive <- FALSE
ELSE
TotalValue <- TotalValue + CurrentReading
ReadingCount <- ReadingCount + 1
ENDIF
UNTIL IsActive = FALSE OR ReadingCount = 50
```

(a) State the type of iteration construct used in this algorithm.

(b) Identify the identifier of the constant used in the algorithm.

(c) State the data type resulting from the expression `CurrentReading > SENSOR_LIMIT`.

(d) State the type of selection construct used in the algorithm.
Show answer & marking scheme

Worked solution

(a) The loop tests its condition at the end after executing at least once, making it a post-condition loop (REPEAT...UNTIL).
(b) The identifier declared with CONSTANT is SENSOR_LIMIT.
(c) The relational operator `>` compares two numeric values and yields a BOOLEAN value (TRUE or FALSE).
(d) The decision-making construct is a selection construct implemented via an IF...THEN...ELSE...ENDIF statement.

Marking scheme

1 mark per correct response:
(a) Post-condition (loop) / REPEAT ... UNTIL [1]
(b) SENSOR_LIMIT [1]
(c) BOOLEAN [1]
(d) IF / IF ... THEN ... ELSE ... ENDIF / (binary) selection [1]
Question 3 · Software Engineering & Lifecycle Theory
11 marks
A logistics company has contracted a software development team to create an automated package weighing and sorting application.

(a) The developers are deciding between using the Waterfall lifecycle model and the Rapid Application Development (RAD) model.

(i) State one defining characteristic of the Waterfall model.

[1]

(ii) Explain one advantage to the client of using the RAD model rather than the Waterfall model for this project.

[2]

(b) Different testing strategies are used during the coding stage.

(i) Describe the difference between white-box testing and black-box testing.

[2]

(ii) Explain the purpose of a stub when testing program modules.

[2]

(c) A function `ValidateWeight()` checks whether a package weight entered in kilograms is valid.

The valid weight range is strictly greater than 0.0 kg and up to a maximum of 50.0 kg inclusive (
0.0 < Weight <= 50.0
).

Give one example of test data for each of the following categories, and state the expected outcome (Accepted or Rejected):

- Normal test data
- Boundary / extreme test data
- Abnormal test data

[3]

(d) Six months after installation, the client requests an enhancement to export monthly dispatch statistics directly to a cloud dashboard. This was not in the original requirements specification.

Identify the type of maintenance that this represents and justify your answer.

[1]
Show answer & marking scheme

Worked solution

(a)(i) Waterfall characteristic (1 mark):
- It is a linear and sequential model where each phase (Analysis, Design, Coding, Testing, Maintenance) must be completed and signed off before the next phase begins.

(a)(ii) Advantage of RAD (2 marks):
- Rapid prototyping is used to provide working mock-ups to the client throughout development.
- This allows requirements to be refined continuously based on direct user feedback, reducing the risk that the final system fails to satisfy business needs.

(b)(i) White-box vs Black-box testing (2 marks):
- White-box testing: Tests the internal logic, algorithms, and code paths; the tester has full visibility of the source code.
- Black-box testing: Tests the program against requirements by providing inputs and examining outputs without inspecting or needing access to the internal code structure.

(b)(ii) Purpose of stub testing (2 marks):
- A stub is a skeleton or dummy routine/procedure with a matching interface that returns dummy values or simple confirmation messages.
- It allows higher-level or dependent modules to be tested and debugged before the subordinate modules have been written.

(c) Test Data Selection (3 marks):
- Normal: Any valid value between 0.0 and 50.0 (e.g. `12.5`, `30.0`), Expected result: Accepted.
- Boundary / extreme: Values on the limits of validity, such as `50.0` (Accepted) or `0.0` / `50.1` (Rejected).
- Abnormal: Invalid values or incorrect types, such as `-4.2`, `85.0`, or `"twenty"`, Expected result: Rejected.

(d) Maintenance (1 mark):
- Perfective maintenance: It involves adding new features, enhancements, or functionality to the system that were not part of the initial specification.

Marking scheme

(a)(i) [Max 1 mark]
- 1 mark for: Linear / sequential progression // each stage completed/signed off before next begins // distinct independent stages.

(a)(ii) [Max 2 marks]
- 1 mark for: Involves iterative development / prototypes / regular user involvement.
- 1 mark for: Enabling early feedback / accommodating changes quickly / ensuring the software meets actual client needs earlier.

(b)(i) [Max 2 marks]
- 1 mark for: White-box testing examines the internal structure / code / logic / all execution paths.
- 1 mark for: Black-box testing tests functionality / input-output specification without knowledge of internal code structure.

(b)(ii) [Max 2 marks]
- 1 mark for: A stub acts as a placeholder / dummy / simplified routine with the correct parameter interface.
- 1 mark for: Allows testing of calling / higher-level modules before the called routine is implemented.

(c) [Max 3 marks]
- 1 mark for: Normal data value (e.g. 15.0 or 25) AND Accepted.
- 1 mark for: Boundary/extreme data value (e.g. 50.0 Accepted, or 0.0 / 50.1 Rejected).
- 1 mark for: Abnormal data value (e.g. -2.5, 75.0, "text") AND Rejected.

(d) [Max 1 mark]
- 1 mark for: Perfective (maintenance) AND justified as adding new functionality / features / improving performance not in original specification.
Note: Do not award mark for Adaptive unless justified solely by environmental/platform change.
Question 4 · Structured English Algorithm Specification
4 marks
A computer system monitors temperature sensors in a botanical greenhouse.

A 1D array, `SensorReadings`, contains 50 `REAL` numbers representing current temperature readings from 50 sensors.

Write an algorithm using structured English to:
- calculate the average temperature of all 50 sensors
- count how many sensors have recorded a temperature greater than 32.0
- determine the maximum temperature recorded
- output `"Ventilation required"` and the maximum temperature if more than 5 sensors recorded a temperature greater than 32.0
- output `"Normal operation"` otherwise.
Show answer & marking scheme

Worked solution

An acceptable structured English description should clearly describe the sequential, iterative, and conditional logic:

1. Initialise `Total` to 0, `HighTempCount` to 0, and `MaxTemp` to the first element (or a suitably low initial value).
2. Loop through all 50 elements of `SensorReadings`:
- Add the current temperature value to `Total`.
- If the current temperature is greater than `MaxTemp`, update `MaxTemp` to the current temperature.
- If the current temperature is greater than 32.0, add 1 to `HighTempCount`.
3. Calculate the average by dividing `Total` by 50.
4. Check if `HighTempCount` is greater than 5:
- If true, output `"Ventilation required"` and `MaxTemp`.
- Otherwise, output `"Normal operation"`.

Marking scheme

1 mark per bullet point (max 4 marks):
- MP1: Initialise accumulators/variables (`Total`, `HighTempCount`, `MaxTemp`) and loop through all 50 array elements.
- MP2: Correctly sum all 50 readings and find the maximum temperature reading.
- MP3: Check if reading > 32.0 and count the occurrences.
- MP4: Post-loop check if count > 5, outputting appropriate messages and the maximum temperature as specified.

Guidance notes:
- Accept equivalent plain English descriptions (e.g. "Iterate through...", "Add 1 to count").
- Do not penalise if pseudocode syntax is used instead of pure structured English, provided the logic is complete and clear.
Question 5 · File Handling & String Pseudocode
11 marks
A text file, `SensorLog.txt`, stores records of temperature readings submitted by remote weather stations. Each line in the file represents a single record in the following format:

`##`

- `StationID` is a four-character string (for example, `"WS04"`).
- `Date` is an eight-character string in the format `"YYYYMMDD"` (for example, `"20241015"`).
- `Reading` is a numeric value stored as a string representing the recorded temperature (for example, `"18.6"` or `"-04.2"`).
- The hash symbol (`#`) acts as a delimiter separating each field.

The following built-in string manipulation functions are available:
- `LENGTH(ThisString : STRING) RETURNS INTEGER` returns the integer count of characters in `ThisString`.
- `MID(ThisString : STRING, Start : INTEGER, Length : INTEGER) RETURNS STRING` returns a string of length `Length` starting at character position `Start` of `ThisString`.
- `STRING_TO_REAL(ThisString : STRING) RETURNS REAL` converts a string representation of a real number into its corresponding real numeric value.

(a) Write pseudocode for a function `ExtractField` that takes two parameters:
- `Record : STRING` (the line containing the delimited record)
- `FieldNum : INTEGER` (the field number to extract: `1`, `2`, or `3`)

The function extracts and returns the string content corresponding to `FieldNum` without any `#` delimiters. [5 marks]

(b) Write pseudocode for a procedure `AnalyseStation(TargetStation : STRING)`.

The procedure must:
- Open the file `SensorLog.txt` for reading.
- Read each record from the file until the end of the file is reached.
- Use the `ExtractField` function to inspect each record.
- For every record where the station ID matches `TargetStation`:
- count the number of matching readings
- calculate the sum of the readings.
- If at least one matching reading was found, calculate and output the average temperature for `TargetStation` alongside an appropriate message.
- If no matching records are found, output `"No records found for specified station."`
- Close the file. [6 marks]
Show answer & marking scheme

Worked solution

(a) Function ExtractField:

```pseudocode
FUNCTION ExtractField(Record : STRING, FieldNum : INTEGER) RETURNS STRING
DECLARE CurrentField : INTEGER
DECLARE ResultStr : STRING
DECLARE NextChar : STRING
DECLARE Index : INTEGER

CurrentField <- 1
ResultStr <- ""

FOR Index <- 1 TO LENGTH(Record)
NextChar <- MID(Record, Index, 1)
IF NextChar = "#" THEN
CurrentField <- CurrentField + 1
ELSE
IF CurrentField = FieldNum THEN
ResultStr <- ResultStr & NextChar
ENDIF
ENDIF
NEXT Index

RETURN ResultStr
ENDFUNCTION
```

(b) Procedure AnalyseStation:

```pseudocode
PROCEDURE AnalyseStation(TargetStation : STRING)
DECLARE CurrentLine : STRING
DECLARE Station : STRING
DECLARE ReadingStr : STRING
DECLARE TotalTemp : REAL
DECLARE AverageTemp : REAL
DECLARE ReadingCount : INTEGER

TotalTemp <- 0.0
ReadingCount <- 0

OPENFILE "SensorLog.txt" FOR READ

WHILE NOT EOF("SensorLog.txt")
READFILE "SensorLog.txt", CurrentLine
Station <- ExtractField(CurrentLine, 1)
IF Station = TargetStation THEN
ReadingStr <- ExtractField(CurrentLine, 3)
TotalTemp <- TotalTemp + STRING_TO_REAL(ReadingStr)
ReadingCount <- ReadingCount + 1
ENDIF
ENDWHILE

CLOSEFILE "SensorLog.txt"

IF ReadingCount > 0 THEN
AverageTemp <- TotalTemp / ReadingCount
OUTPUT "Average temperature for station ", TargetStation, ": ", AverageTemp
ELSE
OUTPUT "No records found for specified station."
ENDIF
ENDPROCEDURE
```

Marking scheme

Part (a) [Max 5 marks]:
- MP1: Correct function header and return declaration: `FUNCTION ExtractField(Record : STRING, FieldNum : INTEGER) RETURNS STRING` and `ENDFUNCTION` / `RETURN` statement returning a string.
- MP2: Loop traversing from 1 to `LENGTH(Record)` using `MID` to inspect individual characters.
- MP3: Tracking field number (e.g. initialised to 1, incremented whenever `'#'` is found).
- MP4: Concatenating characters to the return string only when `CurrentField` equals `FieldNum`.
- MP5: Correctly terminating concatenation or returning the assembled field string.

Part (b) [Max 6 marks]:
- MP1: Correct procedure header `PROCEDURE AnalyseStation(TargetStation : STRING)` and `ENDPROCEDURE`.
- MP2: Opening `"SensorLog.txt"` for `READ` and closing the file with `CLOSEFILE "SensorLog.txt"`.
- MP3: Correct loop structure reading to `EOF("SensorLog.txt")` with `READFILE`.
- MP4: Correctly calling `ExtractField(CurrentLine, 1)` and comparing with `TargetStation`.
- MP5: Converting field 3 to real (`STRING_TO_REAL`) and updating running total and count.
- MP6: Correct conditional check after loop: outputting calculated average when count > 0, and outputting specific error message when count = 0.
Question 6 · structured
11 marks
A customer support desk manages incoming enquiry codes using a Linear Queue Abstract Data Type (ADT). The queue is implemented using a 1D array `QueueData` with 6 elements (indices 1 to 6) and two integer pointer variables:

- `HeadPointer`: stores the array index of the first item in the queue (set to 0 when the queue is empty)
- `TailPointer`: stores the array index of the last item in the queue (set to 0 when the queue is empty)
- `MAX_SIZE`: a constant integer with the value 6

(a) State the condition using the identifier names above that indicates:

(i) the linear queue is empty [1]
(ii) the linear queue is full and cannot accept new items [1]

(b) The procedure `QueueAudit` processes all existing items currently stored in the queue.

```pseudocode
PROCEDURE QueueAudit()
DECLARE CurrentItem : STRING
DECLARE PriorityCount : INTEGER
DECLARE Processed : INTEGER
PriorityCount <- 0
Processed <- 0
WHILE HeadPointer <> 0 AND HeadPointer <= TailPointer
CurrentItem <- QueueData[HeadPointer]
Processed <- Processed + 1
IF MID(CurrentItem, 1, 1) = "P" THEN
PriorityCount <- PriorityCount + 1
OUTPUT "Priority: ", CurrentItem
ELSE
OUTPUT "Standard: ", CurrentItem
ENDIF
HeadPointer <- HeadPointer + 1
ENDWHILE
IF HeadPointer > TailPointer THEN
HeadPointer <- 0
TailPointer <- 0
ENDIF
OUTPUT "Total Processed: ", Processed
OUTPUT "Priority Total: ", PriorityCount
ENDPROCEDURE
```

The initial state of the system is:
- `HeadPointer` = 1
- `TailPointer` = 4
- `QueueData` array contents:
- `QueueData[1]` = "P102"
- `QueueData[2]` = "S205"
- `QueueData[3]` = "P311"
- `QueueData[4]` = "S409"
- `QueueData[5]` = ""
- `QueueData[6]` = ""

Complete the trace table for the execution of `QueueAudit()`. [6]

| PriorityCount | Processed | CurrentItem | HeadPointer | TailPointer | OUTPUT |
|---|---|---|---|---|---|
| | | | 1 | 4 | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |

(c) Explain why inserting items into this linear queue can fail even when empty locations exist in the array, and identify how the data structure design can be modified to resolve this problem without shifting array elements. [3]
Show answer & marking scheme

Worked solution

(a)(i)
Condition: `HeadPointer = 0` (or `HeadPointer = 0 AND TailPointer = 0`).

(a)(ii)
Condition: `TailPointer = MAX_SIZE` (or `TailPointer = 6`).

(b)
Step-by-step trace:
- Initial: `HeadPointer` = 1, `TailPointer` = 4
- Initialisation: `PriorityCount` = 0, `Processed` = 0
- Iteration 1:
`HeadPointer` = 1 (1 <= 4: True)
`CurrentItem` <- "P102"
`Processed` <- 1
`MID("P102", 1, 1)` = "P" -> True
`PriorityCount` <- 1
OUTPUT: `Priority: P102`
`HeadPointer` <- 2
- Iteration 2:
`HeadPointer` = 2 (2 <= 4: True)
`CurrentItem` <- "S205"
`Processed` <- 2
`MID("S205", 1, 1)` = "S" -> False
OUTPUT: `Standard: S205`
`HeadPointer` <- 3
- Iteration 3:
`HeadPointer` = 3 (3 <= 4: True)
`CurrentItem` <- "P311"
`Processed` <- 3
`MID("P311", 1, 1)` = "P" -> True
`PriorityCount` <- 2
OUTPUT: `Priority: P311`
`HeadPointer` <- 4
- Iteration 4:
`HeadPointer` = 4 (4 <= 4: True)
`CurrentItem` <- "S409"
`Processed` <- 4
`MID("S409", 1, 1)` = "S" -> False
OUTPUT: `Standard: S409`
`HeadPointer` <- 5
- Loop terminates as `HeadPointer` (5) <= `TailPointer` (4) is False.
- Post-loop:
`HeadPointer > TailPointer` (5 > 4 is True) -> `HeadPointer` <- 0, `TailPointer` <- 0
OUTPUT: `Total Processed: 4`
OUTPUT: `Priority Total: 2`

(c)
- Reason: As items are dequeued, `HeadPointer` advances towards the end of the array, leaving vacant spaces at indices prior to `HeadPointer`.
- Consequence: When `TailPointer` reaches `MAX_SIZE` (index 6), no further items can be enqueued even though space exists at the beginning.
- Solution: Implement a circular queue (where `TailPointer` wraps around to index 1 using modular arithmetic / reset when space is available).

Marking scheme

(a)(i) [1 mark]
- `HeadPointer = 0` / `HeadPointer = 0 AND TailPointer = 0`

(a)(ii) [1 mark]
- `TailPointer = MAX_SIZE` / `TailPointer = 6`

(b) [6 marks]
- MP1: `PriorityCount` initialised to 0 and correctly updated (1, 2).
- MP2: `Processed` initialised to 0 and correctly incremented (1, 2, 3, 4).
- MP3: `CurrentItem` values correctly assigned sequentially ("P102", "S205", "P311", "S409").
- MP4: `HeadPointer` correctly updated (2, 3, 4, 5) and reset to 0; `TailPointer` reset to 0.
- MP5: Correct 4 enquiry output strings in exact sequence.
- MP6: Correct final summary output strings ("Total Processed: 4", "Priority Total: 2").

(c) [3 marks]
- MP1: Removing/dequeuing items increments `HeadPointer`, leaving unused spaces at the front of the array. [1]
- MP2: `TailPointer` reaches the upper boundary (`MAX_SIZE`), causing an overflow condition / false full state. [1]
- MP3: Re-structure as a circular queue / wrap pointers back to index 1. [1]
Question 7 · structured
11 marks
A linked list ADT is implemented using two 1D arrays: `DataList` of type `STRING` and `PointerList` of type `INTEGER`, with bounds `1:6`. An integer variable `StartPointer` stores the index of the first node in the list, and a pointer value of `-1` represents a null pointer.

The current contents of the data structure are shown below:

| Index | DataList | PointerList |
|---|---|---|
| 1 | "DELTA" | 4 |
| 2 | "ALPHA" | 5 |
| 3 | "" | 6 |
| 4 | "ECHO" | -1 |
| 5 | "BRAVO" | 1 |
| 6 | "" | -1 |

`StartPointer` = 2
`FreeListPointer` = 3

(a) (i) Identify the value of `PointerList[4]` and explain what this value signifies. [2]
(ii) State the logical order of all data values currently stored in the linked list. [1]

(b) The function `CountAndDisplay` searches the linked list:

```pseudocode
FUNCTION CountAndDisplay(SearchChar : CHAR) RETURNS INTEGER
DECLARE CurrentPointer : INTEGER
DECLARE MatchCount : INTEGER
DECLARE CurrentData : STRING
CurrentPointer <- StartPointer
MatchCount <- 0
WHILE CurrentPointer <> -1
CurrentData <- DataList[CurrentPointer]
IF MID(CurrentData, 1, 1) = SearchChar THEN
OUTPUT CurrentData
MatchCount <- MatchCount + 1
ENDIF
CurrentPointer <- PointerList[CurrentPointer]
ENDWHILE
RETURN MatchCount
ENDFUNCTION
```

Complete the trace table for the function call `CountAndDisplay('B')`. [5]

| CurrentPointer | MatchCount | CurrentData | OUTPUT | RETURN value |
|---|---|---|---|---|
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |

(c) Write pseudocode for a procedure `PrintLinkedList()` that traverses the linked list from `StartPointer` and outputs each item alongside its node index in the format:
`Node : `
If the list is empty (`StartPointer = -1`), output `"List is empty"`. [3]
Show answer & marking scheme

Worked solution

(a)(i)
Value: `-1`
Explanation: It is the null pointer / terminator, which indicates that node 4 ("ECHO") is the last node in the linked list.

(a)(ii)
Logical order:
1. Index 2: "ALPHA"
2. Index 5: "BRAVO"
3. Index 1: "DELTA"
4. Index 4: "ECHO"
Order: "ALPHA", "BRAVO", "DELTA", "ECHO"

(b)
Trace of `CountAndDisplay('B')`:
- Initial: `CurrentPointer` <- 2 (`StartPointer`), `MatchCount` <- 0
- Iteration 1:
`CurrentPointer` = 2 (<> -1: True)
`CurrentData` <- `DataList[2]` = "ALPHA"
`MID("ALPHA", 1, 1)` = 'A' <> 'B' (False)
`CurrentPointer` <- `PointerList[2]` = 5
- Iteration 2:
`CurrentPointer` = 5 (<> -1: True)
`CurrentData` <- `DataList[5]` = "BRAVO"
`MID("BRAVO", 1, 1)` = 'B' = 'B' (True)
OUTPUT: `BRAVO`
`MatchCount` <- 1
`CurrentPointer` <- `PointerList[5]` = 1
- Iteration 3:
`CurrentPointer` = 1 (<> -1: True)
`CurrentData` <- `DataList[1]` = "DELTA"
`MID("DELTA", 1, 1)` = 'D' <> 'B' (False)
`CurrentPointer` <- `PointerList[1]` = 4
- Iteration 4:
`CurrentPointer` = 4 (<> -1: True)
`CurrentData` <- `DataList[4]` = "ECHO"
`MID("ECHO", 1, 1)` = 'E' <> 'B' (False)
`CurrentPointer` <- `PointerList[4]` = -1
- Iteration 5:
`CurrentPointer` = -1 (<> -1: False) -> loop terminates
- `RETURN MatchCount` -> returns `1`

(c)
```pseudocode
PROCEDURE PrintLinkedList()
DECLARE CurrentPointer : INTEGER
IF StartPointer = -1 THEN
OUTPUT "List is empty"
ELSE
CurrentPointer <- StartPointer
WHILE CurrentPointer <> -1
OUTPUT "Node ", CurrentPointer, ": ", DataList[CurrentPointer]
CurrentPointer <- PointerList[CurrentPointer]
ENDWHILE
ENDIF
ENDPROCEDURE
```

Marking scheme

(a)(i) [2 marks]
- State `-1` [1]
- States that it indicates the end of the list / null pointer / no next node [1]

(a)(ii) [1 mark]
- "ALPHA", "BRAVO", "DELTA", "ECHO" (in this exact sequence) [1]

(b) [5 marks]
- MP1: `CurrentPointer` column correctly traced: 2, 5, 1, 4, -1 [1]
- MP2: `MatchCount` initialised to 0 and updated to 1 only at node 5 [1]
- MP3: `CurrentData` column correctly traced: "ALPHA", "BRAVO", "DELTA", "ECHO" [1]
- MP4: `OUTPUT` shows "BRAVO" on the correct row [1]
- MP5: `RETURN value` shows 1 upon loop termination [1]

(c) [3 marks]
- MP1: Check if `StartPointer = -1` and output `"List is empty"` [1]
- MP2: Initialise `CurrentPointer <- StartPointer` and loop `WHILE CurrentPointer <> -1` (or equivalent loop structure) [1]
- MP3: Correct output statement combining index and `DataList[CurrentPointer]`, and updating `CurrentPointer <- PointerList[CurrentPointer]` [1]
Question 8 · structured
19 marks
A regional environmental agency monitors meteorological conditions across 15 geographical districts. Each district contains 4 automated sensor nodes.

Data from each sensor node is stored in a record of type `SensorReading`. The record contains the following fields:

- `StationID` : a string representing the unique identifier of the node (e.g. `"D03-N2"`)
- `Temperature` : a real number representing the recorded temperature in degrees Celsius
- `Rainfall` : a real number representing the recorded precipitation in millimetres
- `Active` : a Boolean value indicating whether the sensor is currently operational (`TRUE`) or offline (`FALSE`)

(a) Write pseudocode to define the user-defined record type `SensorReading`. [3]

(b) Write pseudocode declarations for:
- a constant `MAX_DISTRICTS` set to 15
- a constant `MAX_NODES` set to 4
- a global 2D array `GridData` indexed from 1 to `MAX_DISTRICTS` and from 1 to `MAX_NODES` containing elements of type `SensorReading`. [3]

(c) Write pseudocode for a function `CalculateDistrictRainfall`.

The function takes the district index (an integer) as a parameter. It calculates and returns as a real value the total rainfall for all operational (`Active = TRUE`) sensors within that district.
If no sensors in the specified district are active, the function must return `-1.0`. [6]

(d) Write pseudocode for a procedure `GenerateAlerts`.

The procedure must:
- Traverse all districts and nodes in `GridData`.
- Count the total number of inactive sensors across the entire grid.
- Check each district: if any operational sensor in the district records a `Temperature` strictly greater than 40.0, OR if the total rainfall for that district (calculated using `CalculateDistrictRainfall`) strictly exceeds 100.0, output an alert message in the format:
`"ALERT: District " & DistrictNum & " exceeds threshold"`
- Output the total number of inactive sensors across all districts in the format:
`"Total inactive sensors: " & TotalInactive` [7]
Show answer & marking scheme

Worked solution

(a)
```text
TYPE SensorReading
DECLARE StationID : STRING
DECLARE Temperature : REAL
DECLARE Rainfall : REAL
DECLARE Active : BOOLEAN
ENDTYPE
```

(b)
```text
CONSTANT MAX_DISTRICTS = 15
CONSTANT MAX_NODES = 4
DECLARE GridData : ARRAY[1:MAX_DISTRICTS, 1:MAX_NODES] OF SensorReading
```

(c)
```text
FUNCTION CalculateDistrictRainfall(DistrictIndex : INTEGER) RETURNS REAL
DECLARE NodeIndex : INTEGER
DECLARE TotalRain : REAL
DECLARE ActiveCount : INTEGER

TotalRain <- 0.0
ActiveCount <- 0

FOR NodeIndex <- 1 TO MAX_NODES
IF GridData[DistrictIndex, NodeIndex].Active = TRUE THEN
TotalRain <- TotalRain + GridData[DistrictIndex, NodeIndex].Rainfall
ActiveCount <- ActiveCount + 1
ENDIF
NEXT NodeIndex

IF ActiveCount = 0 THEN
RETURN -1.0
ELSE
RETURN TotalRain
ENDIF
ENDFUNCTION
```

(d)
```text
PROCEDURE GenerateAlerts()
DECLARE District : INTEGER
DECLARE Node : INTEGER
DECLARE InactiveCount : INTEGER
DECLARE HighTempFound : BOOLEAN
DECLARE DistrictRain : REAL

InactiveCount <- 0

FOR District <- 1 TO MAX_DISTRICTS
HighTempFound <- FALSE
FOR Node <- 1 TO MAX_NODES
IF GridData[District, Node].Active = FALSE THEN
InactiveCount <- InactiveCount + 1
ELSE
IF GridData[District, Node].Temperature > 40.0 THEN
HighTempFound <- TRUE
ENDIF
ENDIF
NEXT Node

DistrictRain <- CalculateDistrictRainfall(District)
IF HighTempFound = TRUE OR DistrictRain > 100.0 THEN
OUTPUT "ALERT: District ", District, " exceeds threshold"
ENDIF
NEXT District

OUTPUT "Total inactive sensors: ", InactiveCount
ENDPROCEDURE
```

Marking scheme

(a) Record Definition [3 marks]
- 1 mark for `TYPE SensorReading` and `ENDTYPE` header/footer.
- 1 mark for declaring `StationID : STRING` and `Active : BOOLEAN`.
- 1 mark for declaring `Temperature : REAL` and `Rainfall : REAL`.

(b) Declarations [3 marks]
- 1 mark for correct constant declarations (`CONSTANT MAX_DISTRICTS = 15` and `CONSTANT MAX_NODES = 4`).
- 1 mark for declaring array `GridData` with correct 2D bounds `[1:15, 1:4]` (or using the constants `[1:MAX_DISTRICTS, 1:MAX_NODES]`).
- 1 mark for specifying the array type `OF SensorReading`.

(c) Function `CalculateDistrictRainfall` [6 marks]
- 1 mark for correct function header specifying integer parameter and return type `REAL`, with matching `ENDFUNCTION`.
- 1 mark for initializing running total (`TotalRain <- 0.0`) and active counter/flag before loop.
- 1 mark for loop iterating through nodes `1 TO MAX_NODES` (or `1 TO 4`).
- 1 mark for checking `Active = TRUE` and summing `Rainfall` using correct 2D array record syntax `GridData[DistrictIndex, NodeIndex].Rainfall`.
- 1 mark for checking if active count is 0 and returning `-1.0`.
- 1 mark for returning the calculated total if active sensors exist.

(d) Procedure `GenerateAlerts` [7 marks]
- 1 mark for procedure header `PROCEDURE GenerateAlerts()` and `ENDPROCEDURE`.
- 1 mark for nested loops iterating through all districts (`1 TO 15`) and nodes (`1 TO 4`).
- 1 mark for correctly checking `Active = FALSE` and incrementing total inactive sensor count.
- 1 mark for checking operational sensor temperature `> 40.0` and setting a district flag.
- 1 mark for calling `CalculateDistrictRainfall(District)` and testing if total rainfall `> 100.0`.
- 1 mark for conditional statement combining temperature flag `OR` rainfall threshold and outputting correct district alert message.
- 1 mark for outputting the final total inactive sensor count after all loops complete.

Paper 3 Advanced Theory

Answer all questions. Calculators must not be used.
10 Question · 67 marks
Question 1 · Type Declaration
4.5 marks
An autonomous marine research buoy records environmental readings. A programmer designs a program to process these readings using user-defined data types in pseudocode.

(a) Write pseudocode to declare an enumerated data type named `TBuoyStatus` that has four possible values: `ACTIVE`, `CALIBRATING`, `STANDBY`, and `FAULT`.

(b) Write pseudocode to declare a composite record data type named `TReading` containing the following fields:
- `SensorID` : a string
- `ReadingDate` : a date
- `WaterTemp` : a real number
- `Status` : a variable of type `TBuoyStatus`

(c) Write pseudocode to declare a pointer type named `TPointerReading` that points to an element of type `TReading`.
Show answer & marking scheme

Worked solution

(a) An enumerated type is defined using the `TYPE = (, , ...)` syntax:
`TYPE TBuoyStatus = (ACTIVE, CALIBRATING, STANDBY, FAULT)`

(b) A composite record type is defined using `TYPE ... ENDTYPE` enclosing field declarations:
```
TYPE TReading
DECLARE SensorID : STRING
DECLARE ReadingDate : DATE
DECLARE WaterTemp : REAL
DECLARE Status : TBuoyStatus
ENDTYPE
```

(c) A pointer type definition uses the caret symbol `^` preceding the referenced data type:
`TYPE TPointerReading = ^TReading`

Marking scheme

Total Marks: 4.5

Part (a) [1.5 marks]:
- 1 mark for correct keyword `TYPE TBuoyStatus = ` and parentheses surrounding the list.
- 0.5 marks for all 4 identifiers (`ACTIVE`, `CALIBRATING`, `STANDBY`, `FAULT`) separated by commas with no quotes.

Part (b) [2 marks]:
- 0.5 marks for `TYPE TReading` and `ENDTYPE`.
- 0.5 marks for `DECLARE SensorID : STRING` and `DECLARE ReadingDate : DATE`.
- 0.5 marks for `DECLARE WaterTemp : REAL`.
- 0.5 marks for `DECLARE Status : TBuoyStatus` (accepting the user-defined type from part (a)).

Part (c) [1 mark]:
- 1 mark for correct syntax `TYPE TPointerReading = ^TReading` (caret before `TReading`).
Question 2 · Protocol Cloze
4.5 marks
Complete the following technical description of the BitTorrent file-sharing protocol and TCP/IP stack layers by selecting the most appropriate term from the given list. Each term may be used once, more than once, or not at all.

List of terms: `Application`, `bitTorrent`, `checksum`, `client`, `Internet`, `leech`, `Link`, `peer`, `piece`, `seed`, `swarm`, `tracker`, `Transport`

BitTorrent is a peer-to-peer file sharing protocol that operates at the [ 1 ] layer of the TCP/IP stack. When distributing a large file, the file is split into small fragments known as [ 2 ]s.

To coordinate communication among participating computers, a central server known as a [ 3 ] maintains the list of connected peers. All peers actively uploading or downloading parts of the same file form a collective group called a [ 4 ].

A user who has completed downloading the entire file and continues to upload pieces to others is called a [ 5 ], whereas a user who is currently downloading pieces is referred to as a [ 6 ].

Underlying reliable data segment transmission and port addressing for BitTorrent packets are handled by the [ 7 ] layer of the TCP/IP stack, which passes data to the [ 8 ] layer for IP addressing and routing, before frame transmission across physical hardware at the [ 9 ] layer.
Show answer & marking scheme

Worked solution

1: Application — BitTorrent operates at the Application layer where user-facing network applications and protocols function.
2: piece — BitTorrent splits complete files into discrete manageable chunks known as pieces.
3: tracker — A tracker is the central server storing IP addresses of connected clients sharing the torrent metadata.
4: swarm — The collective network of all peers sharing and exchanging a particular file is called a swarm.
5: seed (or seeder) — A peer that has 100% of the file data and actively provides chunks to others.
6: leech (or leecher) — A peer that is currently downloading chunks of the file.
7: Transport — Layer responsible for end-to-end communication, error detection, flow control, and port addressing (e.g., TCP).
8: Internet (or Network) — Layer responsible for logical addressing (IP) and routing packets across networks.
9: Link (or Network Access / Data Link) — Layer responsible for physical transmission of frames over network hardware.

Marking scheme

Total Marks: 4.5 (0.5 marks for each correctly filled gap [1] to [9])

- [1] Application (0.5)
- [2] piece (0.5)
- [3] tracker (0.5)
- [4] swarm (0.5)
- [5] seed (0.5)
- [6] leech (0.5)
- [7] Transport (0.5)
- [8] Internet (0.5)
- [9] Link (0.5)

Guidance:
- Exact spelling required from the provided list.
- Case-insensitive for protocol/layer names (e.g., accept 'application' or 'Application').
Question 3 · theory
6 marks
A logistics firm stores information about parcels in a random access file.

Each record contains a unique 6-digit `ParcelID`, destination code, weight, and delivery status.

(a) Describe how a hashing algorithm is used to determine the storage location when saving a new parcel record to the random file. [3]

(b) A collision occurs when inserting a new record.

(i) State what is meant by a collision in the context of random file organisation. [1]

(ii) Describe how open addressing (linear probing) resolves this collision during insertion. [2]
Show answer & marking scheme

Worked solution

(a)
1. The primary key / `ParcelID` is passed as an argument/input into the hashing function.
2. The algorithm computes a mathematical transformation (e.g., modulo division or folding) to produce a specific home address / record number / bucket index.
3. The system navigates directly to that physical address or record index on secondary storage to write the record.

(b)(i)
A collision happens when two different key values hash to identical bucket/record addresses.

(b)(ii)
1. The hashing function identifies that the initial target location is already occupied by another record.
2. The algorithm steps through contiguous memory/disk locations linearly (i.e. address + 1, address + 2, wrapping around to the start if reaching the end of the file space) until an unoccupied slot is located, and places the record there.

Marking scheme

(a) 1 mark per point to max 3:
- Key field / `ParcelID` is used as an input to the hashing function / mathematical calculation [1]
- The hashing algorithm calculates a record position / bucket address / disk index [1]
- The record is stored directly at that calculated address / position [1]

(b)(i) 1 mark:
- When a hashing algorithm produces the same address / storage location for two different keys / `ParcelID`s [1]

(b)(ii) 1 mark per point to max 2:
- Checks subsequent / next consecutive storage location(s) linearly [1]
- Stores the record in the first empty / available location found (with wraparound to beginning if end is reached) [1]
Question 4 · theory
5 marks
A research laboratory is upgrading its supercomputing cluster to run oceanographic climate simulations.

(a) Identify Flynn's taxonomy classification for a processor architecture where a single control unit executes the same instruction simultaneously across multiple data streams. [1]

(b) Compare SIMD (Single Instruction Multiple Data) and MIMD (Multiple Instruction Multiple Data) architectures. [2]

(c) The laboratory considers adding more processor cores to a massively parallel system. Explain why doubling the number of processors does not typically double the overall processing speed (throughput) of the climate simulation. [2]
Show answer & marking scheme

Worked solution

(a) SIMD (Single Instruction, Multiple Data)

(b)
- SIMD: Single instruction executed simultaneously across multiple processing elements acting on separate data values (common control unit / synchronous).
- MIMD: Multiple processors each executing their own instruction streams independently on separate data streams (autonomous processors / asynchronous).

(c)
1. Non-parallelisable / sequential code bottlenecks: parts of the simulation algorithm must be executed sequentially, limiting the theoretical maximum speedup regardless of processor count.
2. Communication and coordination overhead: as more processors are introduced, the time spent exchanging boundary data, synchronising threads, and managing cache coherency increases, reducing efficiency.

Marking scheme

(a) 1 mark:
- SIMD / Single Instruction Multiple Data [1]

(b) 1 mark per point to max 2:
- SIMD executes one single instruction concurrently across multiple distinct data sets / elements [1]
- MIMD allows multiple processors to execute different instructions on different data sets concurrently / independently [1]

(c) 1 mark per point to max 2:
- Part of the program / algorithm is inherently sequential / cannot be divided into parallel subtasks [1]
- Inter-processor communication overhead / latency increases when coordinating / transferring data between cores [1]
- Contention / bottlenecks in accessing shared memory / bus bandwidth [1]
Question 5 · structured
7.5 marks
A real number is stored in a computer system using a 16-bit two's complement floating-point format:
- 10 bits are allocated to the mantissa.
- 6 bits are allocated to the exponent.

(a) The following binary pattern represents a floating-point number in this format:

`Mantissa: 1011010000`
`Exponent: 000101`

Calculate the denary value of this floating-point number. Show your working. [3]

(b) Convert the denary value \(+26.75\) into a normalised floating-point binary number using the same 16-bit format. Show your working. [3]

(c) State what is meant by floating-point underflow and explain one scenario that causes underflow to occur. [1.5]
Show answer & marking scheme

Worked solution

(a)
1. Evaluate the mantissa (10 bits, two's complement):
\(\text{Mantissa} = 1.011010000_2\)
\(= -1 + 0.25 + 0.125 + 0.03125 = -0.59375\) (or \(-\frac{19}{32}\))
2. Evaluate the exponent (6 bits, two's complement):
\(\text{Exponent} = 000101_2 = +5\)
3. Calculate the denary value:
\(-0.59375 \times 2^5 = -0.59375 \times 32 = -19.0\)

(b)
1. Convert denary \(+26.75\) to fixed-point binary:
\(26 = 11010_2\)
\(0.75 = 0.11_2\)
\(26.75 = 11010.11_2\)
2. Normalise the binary value for a positive number (must start with `01`):
\(11010.11_2 = 0.1101011_2 \times 2^5\)
3. Pad the mantissa to 10 bits: `0110101100`
4. Convert exponent \(+5\) to a 6-bit two's complement binary number: `000101`
Final representation:
Mantissa: `0110101100`, Exponent: `000101`

(c)
Underflow occurs when a numerical calculation yields a non-zero value that is closer to zero than the smallest absolute value that can be represented by the floating-point format (the exponent cannot be made negative enough). Consequently, the value is rounded down to zero.

Marking scheme

(a) 1 mark for each of the following (Max 3):
- MP1: Correct conversion/interpretation of mantissa as \(-0.59375\) or \(-\frac{19}{32}\) (or showing binary shift of 5 places to give `101101.0`).
- MP2: Correct conversion of exponent `000101` to \(+5\).
- MP3: Correct final denary value \(-19\) (or \(-19.0\)).

(b) 1 mark for each of the following (Max 3):
- MP1: Correct conversion of \(26.75\) to fixed-point binary (`11010.11`).
- MP2: Correct normalised 10-bit mantissa (`0110101100`).
- MP3: Correct 6-bit exponent (`000101`).

(c) 1.5 marks (Max 1.5):
- MP1 (1 mark): State that underflow is when a number is too small (magnitude too close to zero) to be represented.
- MP2 (0.5 mark): Reason/scenario — e.g. dividing a very small number by a large number / exponent is smaller than the minimum negative exponent supported.
Question 6 · structured
7.5 marks
A digital circuit in an automated climate-control system has four binary inputs: \(A\), \(B\), \(C\), and \(D\).
The output \(X\) is TRUE (\(1\)) for the following Boolean expression:

\[X = \overline{A}\,\overline{B}\,\overline{C}\,\overline{D} + \overline{A}\,\overline{B}\,\overline{C}\,D + \overline{A}\,B\,\overline{C}\,\overline{D} + \overline{A}\,B\,\overline{C}\,D + \overline{A}\,B\,C\,\overline{D} + A\,B\,\overline{C}\,\overline{D} + A\,B\,\overline{C}\,D + A\,B\,C\,\overline{D}\]

(a) Complete the Karnaugh map (K-map) for the output \(X\). [2]

$$
\begin{array}{|c|c|c|c|c|}
\hline
AB \backslash CD & 00 & 01 & 11 & 10 \\
\hline
00 & & & & \\
\hline
01 & & & & \\
\hline
11 & & & & \\
\hline
10 & & & & \\
\hline
\end{array}
$$

(b) Draw the optimum loops on your Karnaugh map to show the groupings of 1s. [2.5]

(c) Use your groupings from part (b) to write the simplified Sum-of-Products (SOP) Boolean expression for \(X\). [2]

(d) State one benefit of using a Karnaugh map rather than Boolean algebra to simplify logic expressions. [1]
Show answer & marking scheme

Worked solution

(a) Placing 1s into the corresponding minterm cells:
- \(\overline{A}\,\overline{B}\,\overline{C}\,\overline{D}\) \(\rightarrow (00, 00)\)
- \(\overline{A}\,\overline{B}\,\overline{C}\,D\) \(\rightarrow (00, 01)\)
- \(\overline{A}\,B\,\overline{C}\,\overline{D}\) \(\rightarrow (01, 00)\)
- \(\overline{A}\,B\,\overline{C}\,D\) \(\rightarrow (01, 01)\)
- \(\overline{A}\,B\,C\,\overline{D}\) \(\rightarrow (01, 10)\)
- \(A\,B\,\overline{C}\,\overline{D}\) \(\rightarrow (11, 00)\)
- \(A\,B\,\overline{C}\,D\) \(\rightarrow (11, 01)\)
- \(A\,B\,C\,\overline{D}\) \(\rightarrow (11, 10)\)
All other cells are 0.

(b) Grouping of 1s (three overlapping groups of 4):
- Group 1 (Top-left \(2 \times 2\)): cells \((00,00), (00,01), (01,00), (01,01)\) \(\implies \overline{A}\,\overline{C}\)
- Group 2 (Middle-left \(2 \times 2\)): cells \((01,00), (01,01), (11,00), (11,01)\) \(\implies B\,\overline{C}\)
- Group 3 (Rectangle \(2 \times 2\) on rows 01 and 11, cols 00 and 10): cells \((01,00), (01,10), (11,00), (11,10)\) \(\implies B\,\overline{D}\)

(c) Combining the simplified terms:
\(X = \overline{A}\,\overline{C} + B\,\overline{C} + B\,\overline{D}\)

(d) Karnaugh maps provide a visual/graphical method where common terms and minimal groups are immediately apparent, reducing algebraic calculation mistakes.

Marking scheme

(a) 2 marks:
- 1 mark for at least 6 correctly plotted 1s with no incorrect 1s.
- 1 mark for all eight 1s placed correctly and all remaining cells containing 0 (or left blank).

(b) 2.5 marks:
- MP1 (1 mark): Correct group of 4 for \(\overline{A}\,\overline{C}\) (cells `0000, 0001, 0100, 0101`).
- MP2 (1 mark): Correct group of 4 for \(B\,\overline{C}\) (cells `0100, 0101, 1100, 1101`).
- MP3 (0.5 mark): Correct group of 4 for \(B\,\overline{D}\) (cells `0100, 0110, 1100, 1110`).

(c) 2 marks:
- MP1 (1 mark): Any two correct product terms (e.g. \(\overline{A}\,\overline{C} + B\,\overline{C}\)).
- MP2 (1 mark): Fully correct simplified expression \(X = \overline{A}\,\overline{C} + B\,\overline{C} + B\,\overline{D}\) (allow equivalent Boolean notation such as `NOT A AND NOT C OR B AND NOT C OR B AND NOT D`).
(Accept ECF from student's groupings in part (b)).

(d) 1 mark for any valid benefit:
- Easier to identify redundant terms / visual identification of largest groups.
- Less prone to human algebraic manipulation errors.
- Guarantees finding the minimal sum-of-products expression systematically.
Question 7 · theory
7.5 marks
Compilers often convert infix expressions into Reverse Polish Notation (RPN) during the syntax analysis stage.

(a) Convert the following infix expression into RPN:

(a + b * c) / (d - e ^ f)

(b) An expression in RPN is evaluated using a stack:

18 6 / 4 3 * + 5 -

Complete the trace table below to show the contents of the stack after each token is read. The stack grows from left to right (top of stack is on the right).

| Token | Operation / Action | Stack (Bottom -> Top) |
| :--- | :--- | :--- |
| 18 | Push 18 | [ 18 ] |
| 6 | Push 6 | [ 18, 6 ] |
| / | 18 / 6 = 3, Push 3 | [ 3 ] |
| 4 | | |
| 3 | | |
| * | | |
| + | | |
| 5 | | |
| - | | |

(c) Explain two advantages of converting infix expressions to RPN prior to code generation.
Show answer & marking scheme

Worked solution

(a)
1. Evaluate inner multiplication: b * c -> b c *
2. Add a: a + (b * c) -> a b c * +
3. Evaluate exponentiation: e ^ f -> e f ^
4. Subtract from d: d - (e ^ f) -> d e f ^ -
5. Divide left expression by right expression: (a b c * +) / (d e f ^ -) -> a b c * + d e f ^ - /

(b)
- Token 4: Push 4 -> Stack: [ 3, 4 ]
- Token 3: Push 3 -> Stack: [ 3, 4, 3 ]
- Token *: Pop 3, Pop 4, compute 4 * 3 = 12, Push 12 -> Stack: [ 3, 12 ]
- Token +: Pop 12, Pop 3, compute 3 + 12 = 15, Push 15 -> Stack: [ 15 ]
- Token 5: Push 5 -> Stack: [ 15, 5 ]
- Token -: Pop 5, Pop 15, compute 15 - 5 = 10, Push 10 -> Stack: [ 10 ]
Final result = 10.

(c)
1. RPN does not require parentheses / brackets to override default operator precedence.
2. Expressions can be evaluated in a single sequential pass (left to right) using a simple stack without needing to look ahead or backtrack for operator precedence rules.

Marking scheme

(a) 2 marks:
- 1 mark for correct numerator conversion: a b c * +
- 1 mark for correct denominator and final division: d e f ^ - /

(b) 3.5 marks:
- 0.5 mark for each correctly processed step from token 4 to token - (7 rows total / 0.5 per correct stack state & action pair).
- [3, 4] (0.5)
- [3, 4, 3] (0.5)
- Pop 3 & 4, Push 12 -> [3, 12] (0.5)
- Pop 12 & 3, Push 15 -> [15] (0.5)
- Push 5 -> [15, 5] (0.5)
- Pop 5 & 15, Push 10 -> [10] (0.5)
- Stating final evaluated answer as 10 (0.5)

(c) 2 marks (1 mark per valid point explained, max 2):
- It removes the need for brackets / parentheses (1) which simplifies syntax parsing / eliminates ambiguities (1).
- Operators appear in the exact order of execution / precedence is inherently defined (1), so evaluation can be carried out linearly using a stack (1).
- Code generation / translation to low-level stack-machine instructions is much simpler and faster (1).
Question 8 · theory
7.5 marks
Quantum cryptography makes use of quantum mechanics to provide secure communication.

(a) State the primary purpose of Quantum Key Distribution (QKD). [1]

(b) Describe how Quantum Key Distribution (QKD) enables two parties (Sender and Receiver) to generate a shared secret key securely. [4]

(c) Explain why it is impossible for an eavesdropper to intercept the quantum transmission unnoticed. [2.5]
Show answer & marking scheme

Worked solution

(a) The primary purpose of QKD is to establish a shared, secret random key between two communicating endpoints, which can subsequently be used for symmetric encryption (such as a One-Time Pad).

(b)
1. The sender (Alice) emits individual photons polarised in one of several orientations using randomly chosen quantum bases (e.g., rectilinear or diagonal).
2. The photons travel across a dedicated optical/quantum channel (e.g. fibre-optic cable).
3. The receiver (Bob) measures the polarisation of each incoming photon using randomly chosen bases.
4. Alice and Bob communicate over a conventional public channel to disclose and compare which bases they used for each photon (without revealing the actual bit values).
5. They discard the bits where their bases did not match; the remaining bits form the basis of the shared secret key.

(c)
According to the principles of quantum physics (Heisenberg's Uncertainty Principle / No-Cloning Theorem):
- An eavesdropper (Eve) cannot measure/read a photon without altering its quantum state / polarisation.
- Eve cannot create an exact clone/duplicate of an unknown quantum state.
- When Eve intercepts and measures the photon, errors are introduced into the transmission.
- When Alice and Bob test a subset of their shared bits for discrepancies, an unusually high Quantum Bit Error Rate (QBER) will be detected, revealing the presence of the eavesdropper, causing them to discard the compromised key.

Marking scheme

(a) 1 mark:
- To securely generate / exchange / distribute a secret encryption key between two parties (1).

(b) 4 marks (1 mark per bullet, max 4):
- Sender sends a sequence of individual photons with random quantum states / polarisations (using chosen bases) (1).
- Photons are transmitted over an optical link / fibre-optic cable (1).
- Receiver measures the photons using their own randomly selected bases (1).
- Parties communicate over a standard/public channel to compare the bases used (without sharing key bits) (1).
- Bits where the bases matched are retained to generate the shared secret key (1).

(c) 2.5 marks:
- Any attempt by an eavesdropper to intercept / measure / observe a photon changes its quantum state / polarisation (1).
- Quantum states cannot be cloned / copied without measurement (No-Cloning Theorem) (0.5).
- This alteration introduces detectable errors / increases the Quantum Bit Error Rate (QBER) in the received data (1).
- If the error rate exceeds a threshold, the communication is identified as compromised and the key is discarded (1) (Max 2.5 total for part c).
Question 9 · structured
8.5 marks
An autonomous mobile robot navigates an automated warehouse. The layout of the warehouse is represented as a weighted graph where vertices represent transit junctions and edge weights represent travel distances in metres.

(a) State how the node selection strategy differs between Dijkstra's algorithm and the \(A^*\) search algorithm. [2]

(b) The \(A^*\) algorithm uses the evaluation function \(f(n) = g(n) + h(n)\) to determine the next node to expand.
(i) State what \(g(n)\) and \(h(n)\) represent in this function. [2]
(ii) Explain what is meant by an admissible heuristic function in the context of the \(A^*\) algorithm. [1.5]

(c) Describe one advantage and one disadvantage of using the \(A^*\) algorithm instead of Dijkstra's algorithm for route finding in this warehouse environment. [3]
Show answer & marking scheme

Worked solution

(a)
- Dijkstra's algorithm chooses the unvisited node with the minimum accumulated cost from the start node alone, \(g(n)\).
- \(A^*\) algorithm uses both the accumulated cost from the start node and a heuristic estimate of the remaining cost to the goal node, \(f(n) = g(n) + h(n)\).

(b)(i)
- \(g(n)\): The exact cost / distance incurred to travel from the starting node to the current node \(n\).
- \(h(n)\): The estimated heuristic cost / distance from the current node \(n\) to the destination/goal node.

(b)(ii)
- An admissible heuristic never overestimates the true lowest cost (or distance) required to reach the goal node from node \(n\) (i.e. \(h(n) \le h^*(n)\)).

(c)
Advantage:
- More efficient / faster execution time because it directs search towards the goal node, significantly reducing the number of states/nodes evaluated compared to Dijkstra's blind search.
Disadvantage:
- Requires computing a suitable heuristic function, which adds computational overhead per node and can be difficult to design accurately; if the heuristic is poor or non-admissible, it may not guarantee the shortest path.

Marking scheme

(a) 1 mark per point (max 2):
- MP1: Dijkstra evaluates/expands nodes purely on the shortest known distance/cost from the start node / \(g(n)\).
- MP2: \(A^*\) evaluates/expands nodes based on both known cost from the start and an estimated cost to the goal / uses a heuristic / \(f(n) = g(n) + h(n)\).

(b)(i) 1 mark per point (max 2):
- MP1: \(g(n)\) is the actual/known cost from the start node to node \(n\).
- MP2: \(h(n)\) is the estimated/heuristic cost from node \(n\) to the goal node.

(b)(ii) 1.5 marks:
- 1.5 marks for: A heuristic that never overestimates the actual/true cost to reach the goal node.
(Allow 1 mark if candidate states it provides an optimistic estimate without explicitly mentioning non-overestimation).

(c) 3 marks total:
- Advantage (1.5 marks): 1 mark for stating that it visits/explores fewer nodes / reaches the goal faster; 0.5 mark for explaining that the search is directed/focused towards the goal.
- Disadvantage (1.5 marks): 1 mark for identifying increased memory/computational overhead per node OR difficulty in designing an accurate/admissible heuristic; 0.5 mark for linking to potential sub-optimal routes if the heuristic is inadmissible.
Question 10 · structured
8.5 marks
(a) Define the purpose of Big O notation when analysing algorithms. [1.5]

(b) Consider two searching algorithms: Linear Search and Binary Search, each operating on an array containing \(n\) elements.
(i) State the worst-case time complexity, in Big O notation, for Linear Search and Binary Search. [2]
(ii) Explain why Binary Search cannot be directly applied to any arbitrary array without prior preprocessing. [2]

(c) An algorithm can be implemented using either an iterative approach or a recursive approach. Both implementations perform \(n\) sequential operations.
Compare the space complexity (memory usage) of the iterative implementation with that of the recursive implementation. In your answer, explain the underlying mechanism in memory that causes this difference. [3]
Show answer & marking scheme

Worked solution

(a)
- Big O notation expresses the upper bound (or worst-case growth rate) of an algorithm's resource consumption (time or memory) as a mathematical function of input size \(n\).

(b)(i)
- Linear Search: \(O(n)\)
- Binary Search: \(O(\log n)\)

(b)(ii)
- Binary Search requires the elements in the array to be in a sorted / ordered sequence.
- If the array is unsorted, comparing the target value to the midpoint does not reveal which half contains the target, rendering the elimination of search intervals invalid.

(c)
- Iterative space complexity: \(O(1)\) / constant space.
- Recursive space complexity: \(O(n)\) / linear space.
- Mechanism explanation: Each recursive invocation places a new stack frame / activation record onto the call stack containing return addresses, parameters, and local variables. Since \(n\) recursive calls remain active before returning (unwinding), \(O(n)\) memory is consumed on the stack. In contrast, the iterative approach repeatedly updates existing variables in place within a single stack frame.

Marking scheme

(a) 1.5 marks:
- 1 mark for stating it represents the upper bound / worst-case growth rate of execution time or memory.
- 0.5 mark for stating it is a function of the input data size \(n\).

(b)(i) 2 marks:
- 1 mark for Linear Search: \(O(n)\).
- 1 mark for Binary Search: \(O(\log n)\) or \(O(\log_2 n)\).

(b)(ii) 2 marks:
- MP1: Binary search requires the data elements in the array to be sorted / in ascending or descending order (1 mark).
- MP2: If unsorted, the algorithm cannot determine which sub-array / half to eliminate/discard after comparing with the midpoint (1 mark).

(c) 3 marks total:
- MP1: Iterative implementation uses \(O(1)\) auxiliary space / constant memory AND recursive implementation uses \(O(n)\) space / linear memory (1 mark).
- MP2: Explanation that each recursive call generates a new stack frame / activation record on the call stack (1 mark).
- MP3: Explanation that stack frames hold return addresses / local variables / parameters which accumulate until the base case is reached / unwinding begins (1 mark).

Paper 4 Practical

Answer all tasks in Java, Python, or Visual Basic. Save code and screenshots into evidence document.
3 Question · 75 marks
Question 1 · practical
20 marks
A training organisation processes registration records for various professional development workshops.

Each workshop registration is stored as a single string in the format:
`##`

For example: `"Elena Rostova#AI101#85"`

- `` is a string representing the full name.
- `` is a five-character string (e.g. `"AI101"`, `"CY202"`, `"DS303"`).
- `` is an integer value between 1 and 99 representing the priority ranking.

---

### Task 1 (a)
Declare a global 1D array named `WorkshopRecords` capable of storing 10 strings.
Declare a global integer variable named `RecordCount` and initialise it to `0`.

Write a procedure `InitialiseData()` that:
- Populates the array `WorkshopRecords` with the following 8 records:
- `"Amira Khan#CY202#42"`
- `"Lucas Silva#AI101#88"`
- `"Chloe Bennett#DS303#15"`
- `"David O'Connor#CY202#73"`
- `"Elena Rostova#AI101#85"`
- `"Faisal Al-Mansoor#DS303#64"`
- `"Grace Hopper#AI101#91"`
- `"Hassan Patel#CY202#56"`
- Sets `RecordCount` to `8`.

Save your program code.

[3]

---

### Task 1 (b)
Write two functions that use string manipulation to extract individual fields from a record string:

1. `ExtractScore(Record : STRING) RETURNS INTEGER`
- Locates the second `#` delimiter in the string `Record`.
- Extracts the priority score substring following this delimiter.
- Converts the substring to an integer and returns it.

2. `ExtractWorkshop(Record : STRING) RETURNS STRING`
- Locates the first and second `#` delimiters in `Record`.
- Extracts and returns the workshop code substring between them.

Save your program code.

[6]

---

### Task 1 (c)
Write a procedure `SortRecords()` that uses a Bubble Sort algorithm to sort the records currently in `WorkshopRecords` in descending order of their priority score (highest priority score first).

The procedure must:
- Use `ExtractScore()` to compare the priority scores of adjacent elements.
- Swap the entire record strings when an out-of-order pair is found.
- Include an optimisation flag (or early exit mechanism) to terminate sorting early if a pass completes with no swaps.

Save your program code.

[6]

---

### Task 1 (d)
Write program code for the main routine to:
1. Call `InitialiseData()`.
2. Output the header `"--- Records Before Sorting ---"` and display all 8 records in `WorkshopRecords`.
3. Call `SortRecords()`.
4. Output the header `"--- Records After Sorting (Descending Priority) ---"` and display all 8 records in `WorkshopRecords`.
5. Prompt the user to enter a workshop code to search for.
6. Perform a linear search through `WorkshopRecords` and output the participant name and priority score for each matching record.
7. If no matching records are found, output `"No participants registered for this workshop."`

Test your program using the search input: `"CY202"`.

Take a screenshot of the console output and paste it into your evidence document.

[5]
Show answer & marking scheme

Worked solution

### Python 3 Solution

```python
# Task 1 (a)
WorkshopRecords = ["" for _ in range(10)]
RecordCount = 0

def InitialiseData():
global WorkshopRecords, RecordCount
WorkshopRecords[0] = "Amira Khan#CY202#42"
WorkshopRecords[1] = "Lucas Silva#AI101#88"
WorkshopRecords[2] = "Chloe Bennett#DS303#15"
WorkshopRecords[3] = "David O'Connor#CY202#73"
WorkshopRecords[4] = "Elena Rostova#AI101#85"
WorkshopRecords[5] = "Faisal Al-Mansoor#DS303#64"
WorkshopRecords[6] = "Grace Hopper#AI101#91"
WorkshopRecords[7] = "Hassan Patel#CY202#56"
RecordCount = 8

# Task 1 (b)
def ExtractScore(Record):
# Find first and second '#'
first_hash = Record.find('#')
second_hash = Record.find('#', first_hash + 1)
score_str = Record[second_hash + 1:]
return int(score_str)

def ExtractWorkshop(Record):
first_hash = Record.find('#')
second_hash = Record.find('#', first_hash + 1)
return Record[first_hash + 1:second_hash]

def ExtractName(Record):
first_hash = Record.find('#')
return Record[:first_hash]

# Task 1 (c)
def SortRecords():
global WorkshopRecords, RecordCount
n = RecordCount
for i in range(n - 1):
swapped = False
for j in range(0, n - i - 1):
score1 = ExtractScore(WorkshopRecords[j])
score2 = ExtractScore(WorkshopRecords[j + 1])
if score1 < score2: # Descending order
temp = WorkshopRecords[j]
WorkshopRecords[j] = WorkshopRecords[j + 1]
WorkshopRecords[j + 1] = temp
swapped = True
if not swapped:
break

# Task 1 (d)
def main():
InitialiseData()

print("--- Records Before Sorting ---")
for i in range(RecordCount):
print(WorkshopRecords[i])

SortRecords()

print("
--- Records After Sorting (Descending Priority) ---")
for i in range(RecordCount):
print(WorkshopRecords[i])

search_code = input("
Enter workshop code to search: ").strip()
found = False
print(f"
Participants registered for {search_code}:")
for i in range(RecordCount):
if ExtractWorkshop(WorkshopRecords[i]) == search_code:
name = ExtractName(WorkshopRecords[i])
score = ExtractScore(WorkshopRecords[i])
print(f"Name: {name}, Priority: {score}")
found = True

if not found:
print("No participants registered for this workshop.")

if __name__ == "__main__":
main()
```

### Java Solution

```java
import java.util.Scanner;

public class WorkshopManager {
public static String[] WorkshopRecords = new String[10];
public static int RecordCount = 0;

public static void InitialiseData() {
WorkshopRecords[0] = "Amira Khan#CY202#42";
WorkshopRecords[1] = "Lucas Silva#AI101#88";
WorkshopRecords[2] = "Chloe Bennett#DS303#15";
WorkshopRecords[3] = "David O'Connor#CY202#73";
WorkshopRecords[4] = "Elena Rostova#AI101#85";
WorkshopRecords[5] = "Faisal Al-Mansoor#DS303#64";
WorkshopRecords[6] = "Grace Hopper#AI101#91";
WorkshopRecords[7] = "Hassan Patel#CY202#56";
RecordCount = 8;
}

public static int ExtractScore(String record) {
int firstHash = record.indexOf('#');
int secondHash = record.indexOf('#', firstHash + 1);
String scoreStr = record.substring(secondHash + 1);
return Integer.parseInt(scoreStr);
}

public static String ExtractWorkshop(String record) {
int firstHash = record.indexOf('#');
int secondHash = record.indexOf('#', firstHash + 1);
return record.substring(firstHash + 1, secondHash);
}

public static String ExtractName(String record) {
int firstHash = record.indexOf('#');
return record.substring(0, firstHash);
}

public static void SortRecords() {
int n = RecordCount;
for (int i = 0; i < n - 1; i++) {
boolean swapped = false;
for (int j = 0; j < n - i - 1; j++) {
int score1 = ExtractScore(WorkshopRecords[j]);
int score2 = ExtractScore(WorkshopRecords[j + 1]);
if (score1 < score2) {
String temp = WorkshopRecords[j];
WorkshopRecords[j] = WorkshopRecords[j + 1];
WorkshopRecords[j + 1] = temp;
swapped = true;
}
}
if (!swapped) {
break;
}
}
}

public static void main(String[] args) {
InitialiseData();

System.out.println("--- Records Before Sorting ---");
for (int i = 0; i < RecordCount; i++) {
System.out.println(WorkshopRecords[i]);
}

SortRecords();

System.out.println("
--- Records After Sorting (Descending Priority) ---");
for (int i = 0; i < RecordCount; i++) {
System.out.println(WorkshopRecords[i]);
}

Scanner scanner = new Scanner(System.in);
System.out.print("
Enter workshop code to search: ");
String searchCode = scanner.nextLine().trim();

boolean found = false;
System.out.println("
Participants registered for " + searchCode + ":");
for (int i = 0; i < RecordCount; i++) {
if (ExtractWorkshop(WorkshopRecords[i]).equals(searchCode)) {
String name = ExtractName(WorkshopRecords[i]);
int score = ExtractScore(WorkshopRecords[i]);
System.out.println("Name: " + name + ", Priority: " + score);
found = true;
}
}

if (!found) {
System.out.println("No participants registered for this workshop.");
}
}
}
```

Marking scheme

### Mark Scheme Breakdown (Total: 20 marks)

#### Task 1 (a) [3 marks]
- MP1: Correct declaration of 1D global string array `WorkshopRecords` of size 10 (or equivalent fixed size) and integer `RecordCount` initialised to 0.
- MP2: Procedure `InitialiseData()` correctly populates all 8 given record strings into `WorkshopRecords`.
- MP3: Sets `RecordCount` to 8 inside or via `InitialiseData()`.

#### Task 1 (b) [6 marks]
- MP4: `ExtractScore()` finds the position of the second `#` delimiter (e.g. using `find()`, `indexOf()`, or string split).
- MP5: `ExtractScore()` isolates the score portion of the string and converts it to integer.
- MP6: `ExtractScore()` correctly returns the integer score.
- MP7: `ExtractWorkshop()` locates both the first and second `#` delimiters.
- MP8: `ExtractWorkshop()` isolates the substring located strictly between the two delimiters.
- MP9: `ExtractWorkshop()` correctly returns the workshop code string.

#### Task 1 (c) [6 marks]
- MP10: Procedure `SortRecords()` contains nested loops suitable for Bubble Sort over `RecordCount` elements.
- MP11: Calls `ExtractScore()` to extract priority scores of adjacent elements (`j` and `j+1`).
- MP12: Correct comparison condition for descending order (`score[j] < score[j+1]`).
- MP13: Correctly swaps entire record strings within `WorkshopRecords`.
- MP14: Implements a boolean swap flag set to false before inner loop and true when a swap occurs.
- MP15: Correct early termination check (`if not swapped: break` or `while swapped`).

#### Task 1 (d) [5 marks]
- MP16: Calls `InitialiseData()`, displays initial records with appropriate header, and calls `SortRecords()`.
- MP17: Correctly displays sorted records under the specified heading.
- MP18: Prompts user for a workshop code and iterates through records using a linear search.
- MP19: Correctly extracts and displays name and score for all matching workshop records, and outputs the not-found message if no match.
- MP20: Evidence of testing showing execution with input `"CY202"` displaying the before-sort records, descending sorted records, and the 3 matching participants (`David O'Connor` (73), `Hassan Patel` (56), `Amira Khan` (42)).
Question 2 · free-response
25 marks
A university environmental laboratory manages automated monitoring units placed in various ecological reserves. Data from these units is processed using object-oriented programming.

(a) (i) The class `SensorRecord` stores information about an individual monitoring sensor.

The class has four `PRIVATE` attributes:
- `SensorID` : `STRING`
- `StationName` : `STRING`
- `ReadingValue` : `REAL`
- `Status` : `STRING`

Write program code to declare the class `SensorRecord` and its constructor.
The constructor takes `SensorID`, `StationName`, `ReadingValue`, and `Status` as parameters and initialises the respective attributes.

Save your code in your evidence document.
[4]

(a) (ii) Write getter methods for the attributes `SensorID`, `StationName`, `ReadingValue`, and `Status`.

Save your code in your evidence document.
[3]

(a) (iii) Write a method `SetReadingValue` for the class `SensorRecord`.
The method takes a new reading value as a parameter and updates the `ReadingValue` attribute.

Save your code in your evidence document.
[2]

(b) The main program will store up to 50 `SensorRecord` objects in a 1D array named `SensorArray`.

A text file named `Sensors.txt` contains data about sensors. Each line represents one sensor in comma-separated values (CSV) format:
`,,,`

For example:
`SN101,NorthReserve,24.6,Active`

Write a function `ReadSensorData()` that:
- Initialises an array `SensorArray` capable of holding 50 `SensorRecord` objects
- Reads each line from `Sensors.txt`
- Creates an instance of `SensorRecord` for each line read and stores it in `SensorArray`
- Uses exception handling to handle the error if `Sensors.txt` does not exist, displaying an appropriate error message
- Closes the file
- Returns the integer count of records successfully read and stored.

Save your code in your evidence document.
[7]

(c) Write a procedure `DisplayHighReadings` that:
- Takes the array `SensorArray`, the number of records stored, and a threshold reading value (`REAL`) as parameters
- Checks each `SensorRecord` in `SensorArray`
- For each record where `Status` is `"Active"` and `ReadingValue` is greater than or equal to the threshold, outputs `SensorID`, `StationName`, and `ReadingValue` in a formatted message
- Outputs the total number of matching active sensors found.

Save your code in your evidence document.
[5]

(d) Write main program code to:
- Declare `SensorArray` and call `ReadSensorData()`
- Output the number of records successfully loaded
- Prompt the user to enter a threshold reading value and read the value input
- Call `DisplayHighReadings` with the appropriate arguments.

Save your code in your evidence document.
[4]
Show answer & marking scheme

Worked solution

### Python 3 Solution

```python
# (a) (i), (ii), (iii)
class SensorRecord:
def __init__(self, sensor_id, station_name, reading_value, status):
self.__SensorID = str(sensor_id)
self.__StationName = str(station_name)
self.__ReadingValue = float(reading_value)
self.__Status = str(status)

def GetSensorID(self):
return self.__SensorID

def GetStationName(self):
return self.__StationName

def GetReadingValue(self):
return self.__ReadingValue

def GetStatus(self):
return self.__Status

def SetReadingValue(self, new_value):
self.__ReadingValue = float(new_value)


# (b)
SensorArray = [None] * 50

def ReadSensorData():
global SensorArray
count = 0
try:
file = open("Sensors.txt", "r")
for line in file:
line = line.strip()
if line != "":
parts = line.split(",")
if len(parts) == 4 and count < 50:
s_id = parts[0]
s_name = parts[1]
s_val = float(parts[2])
s_status = parts[3]
SensorArray[count] = SensorRecord(s_id, s_name, s_val, s_status)
count += 1
file.close()
except FileNotFoundError:
print("Error: Sensors.txt file not found.")
return count


# (c)
def DisplayHighReadings(sensor_list, num_records, threshold):
matches = 0
for i in range(num_records):
rec = sensor_list[i]
if rec is not None:
if rec.GetStatus() == "Active" and rec.GetReadingValue() >= threshold:
print(f"ID: {rec.GetSensorID()} | Station: {rec.GetStationName()} | Reading: {rec.GetReadingValue()}")
matches += 1
print(f"Total matching active sensors: {matches}")


# (d)
def main():
total_loaded = ReadSensorData()
print(f"{total_loaded} records successfully loaded.")
if total_loaded > 0:
threshold_input = float(input("Enter threshold reading value: "))
DisplayHighReadings(SensorArray, total_loaded, threshold_input)

if __name__ == "__main__":
main()
```

---

### Java Solution

```java
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Solution {
// (a) (i)
public static class SensorRecord {
private String SensorID;
private String StationName;
private double ReadingValue;
private String Status;

public SensorRecord(String sensorID, String stationName, double readingValue, String status) {
this.SensorID = sensorID;
this.StationName = stationName;
this.ReadingValue = readingValue;
this.Status = status;
}

// (a) (ii)
public String GetSensorID() {
return this.SensorID;
}

public String GetStationName() {
return this.StationName;
}

public double GetReadingValue() {
return this.ReadingValue;
}

public String GetStatus() {
return this.Status;
}

// (a) (iii)
public void SetReadingValue(double newValue) {
this.ReadingValue = newValue;
}
}

public static SensorRecord[] SensorArray = new SensorRecord[50];

// (b)
public static int ReadSensorData() {
int count = 0;
try {
File file = new File("Sensors.txt");
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine() && count < 50) {
String line = scanner.nextLine().trim();
if (!line.isEmpty()) {
String[] parts = line.split(",");
if (parts.length == 4) {
String id = parts[0];
String name = parts[1];
double reading = Double.parseDouble(parts[2]);
String status = parts[3];
SensorArray[count] = new SensorRecord(id, name, reading, status);
count++;
}
}
}
scanner.close();
} catch (FileNotFoundException e) {
System.out.println("Error: Sensors.txt not found.");
}
return count;
}

// (c)
public static void DisplayHighReadings(SensorRecord[] arr, int numRecords, double threshold) {
int matches = 0;
for (int i = 0; i < numRecords; i++) {
if (arr[i] != null) {
if (arr[i].GetStatus().equals("Active") && arr[i].GetReadingValue() >= threshold) {
System.out.println("ID: " + arr[i].GetSensorID() + " | Station: " + arr[i].GetStationName() + " | Reading: " + arr[i].GetReadingValue());
matches++;
}
}
}
System.out.println("Total matching active sensors: " + matches);
}

// (d)
public static void main(String[] args) {
int loaded = ReadSensorData();
System.out.println(loaded + " records successfully loaded.");
if (loaded > 0) {
Scanner input = new Scanner(System.in);
System.out.print("Enter threshold reading value: ");
double threshold = input.nextDouble();
DisplayHighReadings(SensorArray, loaded, threshold);
}
}
}
```

Marking scheme

### Part (a) (i) [4 marks]
- MP1: Declaring class `SensorRecord` with four private attributes with appropriate names/types (or private encapsulation prefix in Python).
- MP2: Constructor header taking 4 parameters.
- MP3: Assigning all 4 parameters to their respective internal attributes.
- MP4: Correct types used (`String`, `String`, `Real`/`Float`/`Double`, `String`).

### Part (a) (ii) [3 marks]
- MP1: Four getter methods declared with appropriate names/headers.
- MP2: Each getter returns the corresponding private attribute.
- MP3: Correct syntax for method return values.

### Part (a) (iii) [2 marks]
- MP1: Method header taking one parameter representing new reading.
- MP2: Assigning parameter to `ReadingValue` attribute.

### Part (b) [7 marks]
- MP1: Declaring / using array of size 50 of type `SensorRecord`.
- MP2: Attempting to open `Sensors.txt` in read mode inside `try` block.
- MP3: Reading file line-by-line in a loop until end of file / array limit.
- MP4: Splitting each line by comma delimiter into component fields.
- MP5: Converting reading value to numeric (float/double) and instantiating `SensorRecord` object with parsed data.
- MP6: Storing instantiated object at current index in array and incrementing counter.
- MP7: `catch`/`except` block for file not found with error message AND closing file / returning record count.

### Part (c) [5 marks]
- MP1: Procedure header accepting `SensorArray`, count/size, and `threshold` parameter.
- MP2: Count variable initialised to 0 and loop iterating through valid elements.
- MP3: Correct condition checking status is `"Active"` (case-sensitive/proper comparison).
- MP4: Correct condition checking reading is `>= threshold` and printing sensor details.
- MP5: Outputting total matching count after loop.

### Part (d) [4 marks]
- MP1: Calling `ReadSensorData()` and capturing return value.
- MP2: Printing the number of records returned.
- MP3: Prompting user for threshold value and taking numeric input.
- MP4: Calling `DisplayHighReadings()` with correct parameters.
Question 3 · practical
30 marks
A program stores integer values in an ordered binary search tree.

The binary tree is implemented using a 2D array named `TreeArray` with 15 rows and 3 columns of type INTEGER, where:
- column 0 represents the left child pointer (`LeftPointer`)
- column 1 represents the data value (`Data`)
- column 2 represents the right child pointer (`RightPointer`)

Unused nodes are maintained in a free list. A null pointer is represented by `-1`.
Two global integer pointer variables are used:
- `RootPointer`, initialised to `-1`
- `FreePointer`, initialised to `0`

### Task 1
(a) Write program code to:
- declare the 2D array `TreeArray` globally to hold 15 rows and 3 columns of integers
- declare the global pointers `RootPointer` and `FreePointer`
- write a procedure `InitialiseTree()` that:
- sets `RootPointer` to `-1`
- sets `FreePointer` to `0`
- links all rows in `TreeArray` as a free list such that column 0 of row `i` contains `i + 1`, column 1 contains `0`, and column 2 contains `-1`
- sets column 0 of the last row (index 14) to `-1` to indicate the end of the free list.

Save your program code.
[5 marks]

### Task 2
(b) Write a procedure `InsertNode(NewData : INTEGER)` that inserts `NewData` into the binary tree.

The procedure must:
- check if the free list is full (i.e. `FreePointer` is `-1`). If full, output `"Tree is full"` and exit the procedure
- allocate the next free node from `FreePointer`, update `FreePointer` to point to the next available free node, and initialise the allocated node with `LeftPointer = -1`, `Data = NewData`, and `RightPointer = -1`
- if `RootPointer` is `-1`, set `RootPointer` to the index of this newly allocated node
- otherwise, traverse the tree starting at `RootPointer`:
- if `NewData` is less than the current node's data value, move to its left child; if the left pointer is `-1`, attach the new node as the left child and end traversal
- if `NewData` is greater than or equal to the current node's data value, move to its right child; if the right pointer is `-1`, attach the new node as the right child and end traversal.

Save your program code.
[9 marks]

### Task 3
(c) (i) Write a recursive procedure `InOrder(CurrentPointer : INTEGER)` that performs an in-order traversal of the binary tree starting from `CurrentPointer` and outputs the `Data` value stored at each node.

Save your program code.
[4 marks]

(c) (ii) Write a recursive procedure `PostOrder(CurrentPointer : INTEGER)` that performs a post-order traversal of the binary tree starting from `CurrentPointer` and outputs the `Data` value stored at each node.

Save your program code.
[4 marks]

### Task 4
(d) (i) Write a recursive function `SearchTree(CurrentPointer : INTEGER, SearchValue : INTEGER) RETURNS INTEGER` that searches for `SearchValue` in the binary tree.
- If `CurrentPointer` is `-1`, return `-1`
- If the data at `CurrentPointer` equals `SearchValue`, return `CurrentPointer`
- If `SearchValue` is less than the data at `CurrentPointer`, recursively search the left subtree and return the result
- Otherwise, recursively search the right subtree and return the result.

Save your program code.
[4 marks]

(d) (ii) Write main program code to:
- call `InitialiseTree()`
- insert the following numbers in this order using `InsertNode()`: `45, 23, 67, 12, 34, 56, 89`
- output a suitable heading and call `InOrder(RootPointer)`
- output a suitable heading and call `PostOrder(RootPointer)`
- search for the value `34` using `SearchTree(RootPointer, 34)` and output either `"Found at index "` with the index, or `"Not found"`
- search for the value `99` using `SearchTree(RootPointer, 99)` and output either `"Found at index "` with the index, or `"Not found"`.

Test your program and take a screenshot of the console output.

Save your program code and screenshot in your evidence document.
[4 marks]
Show answer & marking scheme

Worked solution

### Python Solution

```python
# Global declarations
TreeArray = [[0 for _ in range(3)] for _ in range(15)]
RootPointer = -1
FreePointer = 0

def InitialiseTree():
global TreeArray, RootPointer, FreePointer
RootPointer = -1
FreePointer = 0
for i in range(14):
TreeArray[i][0] = i + 1
TreeArray[i][1] = 0
TreeArray[i][2] = -1
TreeArray[14][0] = -1
TreeArray[14][1] = 0
TreeArray[14][2] = -1

def InsertNode(NewData):
global TreeArray, RootPointer, FreePointer
if FreePointer == -1:
print("Tree is full")
return

# Allocate new node
NewNodeIndex = FreePointer
FreePointer = TreeArray[FreePointer][0]
TreeArray[NewNodeIndex][0] = -1
TreeArray[NewNodeIndex][1] = NewData
TreeArray[NewNodeIndex][2] = -1

if RootPointer == -1:
RootPointer = NewNodeIndex
else:
CurrentPointer = RootPointer
Placed = False
while not Placed:
if NewData < TreeArray[CurrentPointer][1]:
if TreeArray[CurrentPointer][0] == -1:
TreeArray[CurrentPointer][0] = NewNodeIndex
Placed = True
else:
CurrentPointer = TreeArray[CurrentPointer][0]
else:
if TreeArray[CurrentPointer][2] == -1:
TreeArray[CurrentPointer][2] = NewNodeIndex
Placed = True
else:
CurrentPointer = TreeArray[CurrentPointer][2]

def InOrder(CurrentPointer):
global TreeArray
if CurrentPointer != -1:
InOrder(TreeArray[CurrentPointer][0])
print(TreeArray[CurrentPointer][1])
InOrder(TreeArray[CurrentPointer][2])

def PostOrder(CurrentPointer):
global TreeArray
if CurrentPointer != -1:
PostOrder(TreeArray[CurrentPointer][0])
PostOrder(TreeArray[CurrentPointer][2])
print(TreeArray[CurrentPointer][1])

def SearchTree(CurrentPointer, SearchValue):
global TreeArray
if CurrentPointer == -1:
return -1
if TreeArray[CurrentPointer][1] == SearchValue:
return CurrentPointer
elif SearchValue < TreeArray[CurrentPointer][1]:
return SearchTree(TreeArray[CurrentPointer][0], SearchValue)
else:
return SearchTree(TreeArray[CurrentPointer][2], SearchValue)

# Main Program
InitialiseTree()
values_to_insert = [45, 23, 67, 12, 34, 56, 89]
for val in values_to_insert:
InsertNode(val)

print("In-Order Traversal:")
InOrder(RootPointer)

print("Post-Order Traversal:")
PostOrder(RootPointer)

# Search 34
idx = SearchTree(RootPointer, 34)
if idx != -1:
print(f"Found at index {idx}")
else:
print("Not found")

# Search 99
idx2 = SearchTree(RootPointer, 99)
if idx2 != -1:
print(f"Found at index {idx2}")
else:
print("Not found")
```

Marking scheme

### Task 1 (a) [Max 5 marks]
- MP1: Declaring 2D array of size 15 by 3 (or 15 rows, 3 columns) of INTEGER data type.
- MP2: Declaring `RootPointer` and `FreePointer` as global integers.
- MP3: Procedure header `InitialiseTree()` and setting `RootPointer = -1`, `FreePointer = 0`.
- MP4: Iterating 0 to 13 (or 1 to 14) and setting `TreeArray[i][0] = i + 1`, `TreeArray[i][1] = 0`, `TreeArray[i][2] = -1`.
- MP5: Setting column 0 of last element (index 14) to `-1`.

### Task 2 (b) [Max 9 marks]
- MP1: Procedure header `InsertNode(NewData)` with integer parameter.
- MP2: Checking if `FreePointer == -1` and outputting `"Tree is full"` / returning.
- MP3: Storing `FreePointer` into `NewNodeIndex` and advancing `FreePointer` to `TreeArray[FreePointer][0]`.
- MP4: Initialising `TreeArray[NewNodeIndex]` left pointer to `-1`, data to `NewData`, right pointer to `-1`.
- MP5: Checking if `RootPointer == -1` and setting `RootPointer = NewNodeIndex`.
- MP6: Traversal loop (WHILE loop or equivalent) initialising current pointer to `RootPointer`.
- MP7: Correct conditional branch for `NewData < CurrentNode Data` vs `>=`.
- MP8: Correctly checking if child pointer is `-1`, attaching `NewNodeIndex` and setting flag/terminating loop.
- MP9: Advancing `CurrentPointer` to child pointer when pointer is not `-1`.

### Task 3 (c)(i) [Max 4 marks]
- MP1: Procedure header `InOrder(CurrentPointer)` with integer parameter.
- MP2: Base case / guard condition (`CurrentPointer != -1`).
- MP3: Recursive call on left child (`TreeArray[CurrentPointer][0]`) before outputting data.
- MP4: Outputting node's data value and recursive call on right child (`TreeArray[CurrentPointer][2]`) after outputting.

### Task 3 (c)(ii) [Max 4 marks]
- MP1: Procedure header `PostOrder(CurrentPointer)` with integer parameter.
- MP2: Base case / guard condition (`CurrentPointer != -1`).
- MP3: Recursive calls on left child and then right child.
- MP4: Outputting node's data value after both recursive child calls.

### Task 4 (d)(i) [Max 4 marks]
- MP1: Function header `SearchTree(CurrentPointer, SearchValue)` returning integer.
- MP2: Base case returning `-1` when `CurrentPointer == -1`.
- MP3: Returning `CurrentPointer` when `TreeArray[CurrentPointer][1] == SearchValue`.
- MP4: Correct recursive calls returning result for left subtree if `SearchValue < Data` and right subtree otherwise.

### Task 4 (d)(ii) [Max 4 marks]
- MP1: Calling `InitialiseTree()` and calling `InsertNode()` with all 7 specified values in the correct order.
- MP2: Calling `InOrder(RootPointer)` and `PostOrder(RootPointer)` with appropriate headings.
- MP3: Calling `SearchTree` for values `34` and `99` and outputting found/not found conditionally.
- MP4: Screenshot evidence showing correct in-order sequence `(12, 23, 34, 45, 56, 67, 89)`, post-order sequence `(12, 34, 23, 56, 89, 67, 45)`, index found for 34 (index 4), and not found message for 99.

Wondering how well you actually know this?

thinka is an AI practice app for GCSE & A-Level students: unlimited questions, instant auto-marking, and detailed step-by-step solutions. 100,000+ students use it to confirm they actually know it, not just think they do.

Want more questions like this? Practise unlimited on thinka, instant answers included.

Start Practising Free