An original Thinka practice paper modelled on the structure and difficulty of the Nov 2025 (V1) Cambridge International A Level Computer Science (9618) paper. Not affiliated with or reproduced from Cambridge.
Paper 11: AS Theory Fundamentals
Answer all questions. Calculators must not be used. Total marks: 75.
10 Question · 75 marks
Question 1 · Short Answer & Trace
7.5 marks
a) Convert the denary value \(-87\) into an 8-bit two's complement binary integer. Show your working.
b) Binary Coded Decimal (BCD) is a system used to represent denary digits. i) State one practical application of BCD. ii) Convert the denary number \(294\) into its BCD representation.
Show answer & marking schemeHide answer & marking scheme
Worked solution
a) To find the 8-bit two's complement representation of \(-87\): 1. Represent positive \(+87\) in binary: \(87 = 64 + 16 + 4 + 2 + 1 = 01010111_2\). 2. Find the one's complement (invert the bits): \(10101000_2\). 3. Add 1 to get the two's complement: \(10101000 + 1 = 10101001_2\). Therefore, \(-87\) is represented as `10101001`.
b) i) BCD is used in applications where precise decimal representation is necessary without rounding errors, such as in calculator displays, digital clocks, and financial software. ii) To convert \(294\) to BCD, convert each denary digit individually to a 4-bit binary nibble: - \(2 \rightarrow 0010\) - \(9 \rightarrow 1001\) - \(4 \rightarrow 0100\) Putting these together gives `0010 1001 0100`.
Marking scheme
Total: 7.5 Marks
Part a) [3 Marks] - 1 mark for correct binary representation of \(+87\) (01010111) or finding one's complement (10101000). - 1 mark for adding 1 correctly to their binary representation. - 1 mark for correct final 8-bit answer: 10101001.
Part b) i) [1.5 Marks] - 1.5 marks for naming a correct application (e.g., pocket calculators, digital clocks, currency/financial systems, or displaying decimal numbers on 7-segment displays).
Part b) ii) [3 Marks] - 1 mark for converting 2 to 0010. - 1 mark for converting 9 to 1001. - 1 mark for converting 4 to 0100. (Accept the combined answer 001010010100 with or without spaces).
Question 2 · Short Answer & Trace
7.5 marks
An assembly language program is executed. The following table shows the current contents of selected memory addresses:
Trace the execution of the following assembly language instructions, showing the contents of the Accumulator (ACC) and the affected memory locations after each instruction is executed. Assume the ACC starts at 0.
```assembly LDD 151 ADD 153 STO 150 LDI 151 SUB 152 STO 151 ```
Show the values of ACC and affected memory locations after each instruction.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Let us trace each step: 1. `LDD 151`: Load direct from address 151. Memory[151] contains 150. ACC becomes 150. 2. `ADD 153`: Add direct from address 153. Memory[153] contains 5. ACC becomes 150 + 5 = 155. 3. `STO 150`: Store accumulator in address 150. Memory[150] becomes 155. (ACC remains 155). 4. `LDI 151`: Load indirect from address 151. Address 151 contains 150, so we load the value from address 150. Memory[150] was changed to 155 in step 3. So ACC becomes 155. 5. `SUB 152`: Subtract direct from address 152. Memory[152] contains 15. ACC becomes 155 - 15 = 140. 6. `STO 151`: Store accumulator in address 151. Memory[151] becomes 140.
Marking scheme
Total: 7.5 Marks
- 1 mark for step 1: ACC = 150. - 1 mark for step 2: ACC = 155. - 1.5 marks for step 3: Memory[150] updated to 155. - 1.5 marks for step 4: ACC = 155 (correctly loading indirect from address 150 using value in 151). - 1 mark for step 5: ACC = 140. - 1.5 marks for step 6: Memory[151] updated to 140.
Question 3 · Short Answer & Trace
7.5 marks
A database table `STUDENT_TRIP` stores information about school field trips. The unnormalized table has the following attributes:
The business rules are: - Each student has a unique `StudentID` and one `StudentName`. - Each trip has a unique `TripID`, a single `TripDestination`, and a single `TripCost`. - A student can register for multiple trips, and a trip can have multiple students registered.
a) Explain why the `STUDENT_TRIP` table is not in Second Normal Form (2NF). (2.5 marks)
b) Normalize the database representation to Third Normal Form (3NF). Write the schemas of the resulting tables, clearly identifying the primary keys (PK) and foreign keys (FK) for each table. (5 marks)
Show answer & marking schemeHide answer & marking scheme
Worked solution
a) A table is in 2NF if it is in 1NF and contains no partial functional dependencies (i.e., every non-prime attribute is fully functionally dependent on the entire primary key). In this table, the composite primary key is `(StudentID, TripID)`. However: - `StudentName` depends only on `StudentID` (part of the PK). - `TripDestination` and `TripCost` depend only on `TripID` (part of the PK). Since partial dependencies exist, the table is not in 2NF.
b) To normalize to 3NF, we must eliminate these partial dependencies: 1. Create a `STUDENT` table to store student details: `STUDENT(StudentID, StudentName)` where `StudentID` is the Primary Key. 2. Create a `TRIP` table to store trip details: `TRIP(TripID, TripDestination, TripCost)` where `TripID` is the Primary Key. 3. Create an associative table `REGISTRATION` (or `STUDENT_TRIP`) to link students to trips: `REGISTRATION(StudentID, TripID)` where `(StudentID, TripID)` is the composite Primary Key, and `StudentID` and `TripID` are Foreign Keys referencing the `STUDENT` and `TRIP` tables respectively.
Marking scheme
Total: 7.5 Marks
Part a) [2.5 Marks] - 1 mark for defining partial dependency / stating that non-prime attributes must depend on the whole composite key. - 1.5 marks for explaining the specific partial dependencies: `StudentName` depends only on `StudentID`, and/or `TripDestination`/`TripCost` depend only on `TripID`.
Part b) [5 Marks] - 1.5 marks for the STUDENT relation with Primary Key `StudentID` identified. - 1.5 marks for the TRIP relation with Primary Key `TripID` identified. - 2 marks for the intersection relation (e.g., REGISTRATION) with composite Primary Key `(StudentID, TripID)` and both identified as Foreign Keys (FK).
Question 4 · Short Answer & Trace
7.5 marks
Client-side and server-side scripting are fundamental to the operation of dynamic websites.
a) State where the execution takes place for both client-side and server-side scripting, and identify one common programming language used for each. (3.5 marks)
b) For each of the following scenarios, identify whether client-side scripting or server-side scripting is most appropriate, and briefly justify your choice: i) Checking if a mandatory field in a contact form is empty before submission. ii) Querying a secure database to retrieve a customer's account balance. (4 marks)
Show answer & marking schemeHide answer & marking scheme
Worked solution
a) - Client-side scripting: Executed in the web browser of the client machine. Typical language: JavaScript. - Server-side scripting: Executed on the web server. Typical languages: PHP, Python, ASP.NET, Ruby, Java, or Node.js.
b) - i) Form validation (empty field check): Client-side. Justification: Provides instant feedback to the user, improves user experience, and reduces unnecessary network traffic and server workload. - ii) Retrieving account balance: Server-side. Justification: Database connections require secret credentials (username/password) which must never be exposed to the client-side browser for security. Processing must be done securely on the server.
Marking scheme
Total: 7.5 Marks
Part a) [3.5 Marks] - 1 mark for stating client-side executes in the web browser. - 1 mark for stating server-side executes on the web server. - 1.5 marks for naming one correct language for each (e.g., JavaScript for client-side [0.75 marks]; PHP/Python/SQL/Java/Node.js for server-side [0.75 marks]).
Part b) [4 Marks] - i) 1 mark for identifying client-side. - 1 mark for correct justification (instant feedback / reduces server load / reduces network traffic). - ii) 1 mark for identifying server-side. - 1 mark for correct justification (security of database credentials / client should not have direct database access).
Question 5 · Short Answer & Trace
7.5 marks
a) Construct the truth table for the output \(X\) of the following Boolean expression:
\(X = (\text{NOT } A \text{ AND } B) \text{ OR } (B \text{ XOR } C)\)
Include intermediate columns for \(P = \text{NOT } A \text{ AND } B\) and \(Q = B \text{ XOR } C\). (5.5 marks)
b) Simplify the Boolean expression \(Y = A \cdot B + A \cdot \overline{B}\) using Boolean algebra laws. Show your working and state the laws used. (2 marks)
Show answer & marking schemeHide answer & marking scheme
Worked solution
a) Let's calculate the values systematically: - Row 1: A=0, B=0, C=0. P = NOT 0 AND 0 = 0. Q = 0 XOR 0 = 0. X = 0 OR 0 = 0. - Row 2: A=0, B=0, C=1. P = NOT 0 AND 0 = 0. Q = 0 XOR 1 = 1. X = 0 OR 1 = 1. - Row 3: A=0, B=1, C=0. P = NOT 0 AND 1 = 1. Q = 1 XOR 0 = 1. X = 1 OR 1 = 1. - Row 4: A=0, B=1, C=1. P = NOT 0 AND 1 = 1. Q = 1 XOR 1 = 0. X = 1 OR 0 = 1. - Row 5: A=1, B=0, C=0. P = NOT 1 AND 0 = 0. Q = 0 XOR 0 = 0. X = 0 OR 0 = 0. - Row 6: A=1, B=0, C=1. P = NOT 1 AND 0 = 0. Q = 0 XOR 1 = 1. X = 0 OR 1 = 1. - Row 7: A=1, B=1, C=0. P = NOT 1 AND 1 = 0. Q = 1 XOR 0 = 1. X = 0 OR 1 = 1. - Row 8: A=1, B=1, C=1. P = NOT 1 AND 1 = 0. Q = 1 XOR 1 = 0. X = 0 OR 0 = 0.
b) To simplify \(Y = A \cdot B + A \cdot \overline{B}\): 1. Factor out \(A\) using the Distributive Law: \(Y = A \cdot (B + \overline{B})\). 2. Apply the Complement Law (\(B + \overline{B} = 1\)): \(Y = A \cdot 1\). 3. Apply the Identity Law (\(A \cdot 1 = A\)): \(Y = A\).
Marking scheme
Total: 7.5 Marks
Part a) [5.5 Marks] - 1.5 marks for correct column \(P\): (0, 0, 1, 1, 0, 0, 0, 0). - 1.5 marks for correct column \(Q\): (0, 1, 1, 0, 0, 1, 1, 0). - 2.5 marks for correct final column \(X\): (0, 1, 1, 1, 0, 1, 1, 0) (subtract 0.5 marks for each error up to max 2.5).
Part b) [2 Marks] - 1 mark for applying Distributive law to factor out \(A\): \(A(B + \overline{B})\). - 1 mark for correct final simplification \(A\) with correct law named (Complement or Identity law).
Question 6 · Short Answer & Trace
7.5 marks
Operating systems and utility programs are essential components of system software.
a) Compare a compiler and an interpreter by identifying three distinct operational differences. (4.5 marks)
b) A user's hard disk has become fragmented over time, leading to slower system performance. i) State the name of the utility program that can resolve this issue. ii) Explain how this utility program reorganizes the storage to improve performance. (3 marks)
Show answer & marking schemeHide answer & marking scheme
Worked solution
a) Key differences between a Compiler and an Interpreter: - **Translation Mechanism**: A compiler translates the whole source code into an independent machine code executable file (object code) once. An interpreter translates source code statement by statement (or line by line) directly into intermediate actions and executes it immediately. - **Execution Speed**: Compiled programs run much faster because the translation has already occurred. Interpreted programs run more slowly because translation is performed on-the-fly during execution. - **Error Detection**: A compiler analyzes the entire program and lists all errors at the end of compilation. An interpreter analyzes code line by line and halts execution the moment it finds an error, making debugging easier but execution incomplete. - **Resource Usage**: A compiler requires significant memory during compilation but the executable runs independently. An interpreter must be present in memory during program execution.
b) i) Disk Defragmenter. ii) Over time, files are written, modified, and deleted, causing file data to be scattered across non-contiguous sectors on the disk (fragmentation). The defragmenter reorganizes these file blocks so that the data for each file is stored in contiguous sectors. This minimizes the movement of the physical drive read/write head, significantly reducing access times.
Marking scheme
Total: 7.5 Marks
Part a) [4.5 Marks] - 1.5 marks per distinct comparative point, up to a maximum of 3 points (total 4.5 marks). Must contrast both compiler and interpreter to get full marks for a point. - Point 1: Execution/Translation (Whole file vs Line-by-line) - Point 2: Speed (Faster execution vs Slower execution) - Point 3: Error handling (Reports all errors at end vs Stops on first error) - Point 4: Output (Creates an executable file vs Does not produce an executable)
Part b) [3 Marks] - i) 1 mark for identifying "Disk Defragmenter" (or Defragmentation utility). - ii) 2 marks for explanation: - 1 mark for stating that it reorganizes scattered/non-contiguous file blocks into contiguous (consecutive) sectors. - 1 mark for stating that it groups free space together / reduces read-write head travel time.
Question 7 · Short Answer & Trace
7.5 marks
A linear queue is implemented as a static data structure using an array `Names[0..4]` and two integer pointers, `Head` and `Tail`.
- `Head` points to the index of the element at the front of the queue. - `Tail` points to the index of the last element added to the queue. - When the queue is empty, `Head` is set to `0` and `Tail` is set to `-1`.
a) State the maximum number of elements this queue can hold. (1 mark)
b) Show the changes to the array `Names` and the pointers `Head` and `Tail` by completing the trace table below for the sequence of operations.
Show answer & marking schemeHide answer & marking scheme
Worked solution
a) The array indices are `0..4`, which means there are 5 indices (0, 1, 2, 3, 4). Thus, the maximum number of elements is 5.
b) Tracing the operations step-by-step for a linear queue: - Initial state: Head = 0, Tail = -1. 1. `Enqueue("Alice")`: - Tail increments to 0. - `Names[0]` becomes "Alice". - Head remains 0. 2. `Enqueue("Bob")`: - Tail increments to 1. - `Names[1]` becomes "Bob". - Head remains 0. 3. `Enqueue("Charlie")`: - Tail increments to 2. - `Names[2]` becomes "Charlie". - Head remains 0. 4. `Dequeue()`: - The element at Head (Names[0], "Alice") is removed/ignored. - Head increments to 1. - Tail remains 2. 5. `Enqueue("David")`: - Tail increments to 3. - `Names[3]` becomes "David". - Head remains 1. 6. `Dequeue()`: - The element at Head (Names[1], "Bob") is removed/ignored. - Head increments to 2. - Tail remains 3.
Marking scheme
Total: 7.5 Marks
Part a) [1 Mark] - 1 mark for stating the maximum number of elements is 5.
Part b) [6.5 Marks] - 1 mark for row 1: Head = 0, Tail = 0, Names[0] = "Alice". - 1 mark for row 2: Head = 0, Tail = 1, Names[1] = "Bob" (Names[0] remains "Alice"). - 1 mark for row 3: Head = 0, Tail = 2, Names[2] = "Charlie". - 1 mark for row 4 (Dequeue): Head increments to 1, Tail remains 2. (Names[0] can be shown as empty/crossed out, but Names[1] and Names[2] must remain). - 1 mark for row 5: Head = 1, Tail = 3, Names[3] = "David". - 1.5 marks for row 6 (Dequeue): Head increments to 2, Tail remains 3.
Question 8 · Short Answer & Trace
7.5 marks
A programmer has written a pseudocode algorithm to reverse a portion of a 1D array.
```pseudocode DECLARE Numbers : ARRAY[0..5] OF INTEGER DECLARE First, Last, Temp : INTEGER
WHILE First < Last DO Temp <- Numbers[First] Numbers[First] <- Numbers[Last] Numbers[Last] <- Temp First <- First + 1 Last <- Last - 1 ENDWHILE ```
Trace the execution of this pseudocode. Complete the trace table below by showing the values of the variables `First`, `Last`, `Temp`, and the contents of the `Numbers` array after each change. Only enter values when they change.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Let's trace step-by-step: - Array starts as `[12, 45, 30, 8, 15, 90]` - `First <- 1`, `Last <- 4` - Check condition `First < Last` -> `1 < 4` is True. - `Temp <- Numbers[First]` -> `Temp <- Numbers[1]` which is `45`. - `Numbers[First] <- Numbers[Last]` -> `Numbers[1] <- Numbers[4]` which is `15`. (Array is now `[12, 15, 30, 8, 15, 90]`) - `Numbers[Last] <- Temp` -> `Numbers[4] <- 45`. (Array is now `[12, 15, 30, 8, 45, 90]`) - `First <- First + 1` -> `First <- 2` - `Last <- Last - 1` -> `Last <- 3` - Check condition `First < Last` -> `2 < 3` is True. - `Temp <- Numbers[First]` -> `Temp <- Numbers[2]` which is `30`. - `Numbers[First] <- Numbers[Last]` -> `Numbers[2] <- Numbers[3]` which is `8`. (Array is now `[12, 15, 8, 8, 45, 90]`) - `Numbers[Last] <- Temp` -> `Numbers[3] <- 30`. (Array is now `[12, 15, 8, 30, 45, 90]`) - `First <- First + 1` -> `First <- 3` - `Last <- Last - 1` -> `Last <- 2` - Check condition `First < Last` -> `3 < 2` is False. - The loop terminates. Final array: `[12, 15, 8, 30, 45, 90]`.
Marking scheme
Total: 7.5 Marks
- 1 mark for correctly setting initial `First = 1` and `Last = 4`. - 1 mark for `Temp = 45` in the first iteration. - 1.5 marks for swapping `Numbers[1]` and `Numbers[4]` (showing values 15 and 45 respectively). - 1 mark for updating `First = 2` and `Last = 3` at the end of the first iteration. - 1 mark for `Temp = 30` in the second iteration. - 1.5 marks for swapping `Numbers[2]` and `Numbers[3]` (showing values 8 and 30 respectively). - 0.5 marks for updating `First = 3` and `Last = 2` and terminating the loop correctly.
Question 9 · Short Answer & Trace
7.5 marks
An assembler uses the following instruction set: LDM #n (Load number n into ACC), LDD (Load contents of address into ACC), LDX (Load contents of address + IX into ACC), STO (Store ACC in address), ADD (Add contents of address to ACC), INC (Add 1 to specified register). The initial state of memory and registers is: Index Register (IX) = 1, Accumulator (ACC) = 0, Memory address 120 = 10, Memory address 121 = 22, Memory address 122 = 35, Memory address 123 = 40, Memory address 150 = 0. (a) Complete the trace table for the execution of the following assembly code: LDM #5; STO 150; LDX 120; ADD 150; STO 150; INC IX; LDX 120; ADD 150; STO 150. (b) Explain how the memory address is calculated when the instruction LDX 120 is executed for the first time.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Let's perform the trace line by line: Initial state: ACC = 0, IX = 1, Mem[150] = 0. - LDM #5: Loads immediate value 5 into ACC. ACC becomes 5. - STO 150: Stores ACC (5) in address 150. Mem[150] becomes 5. - LDX 120: Calculates address as 120 + IX = 120 + 1 = 121. Loads contents of 121 (22) into ACC. ACC becomes 22. - ADD 150: Adds contents of 150 (5) to ACC (22). ACC becomes 27. - STO 150: Stores ACC (27) in address 150. Mem[150] becomes 27. - INC IX: Increments IX by 1. IX becomes 2. - LDX 120: Calculates address as 120 + IX = 120 + 2 = 122. Loads contents of 122 (35) into ACC. ACC becomes 35. - ADD 150: Adds contents of 150 (27) to ACC (35). ACC becomes 62. - STO 150: Stores ACC (62) in address 150. Mem[150] becomes 62.
Marking scheme
Part a (6 marks in total): - 1 mark for correct ACC and Mem[150] after LDM #5 and STO 150. - 1 mark for correct ACC (22) after the first LDX 120. - 1 mark for correct ACC (27) after ADD 150. - 1 mark for correct Mem[150] (27) and updated IX (2). - 1 mark for correct ACC (35) after the second LDX 120. - 1 mark for final ACC (62) and Mem[150] (62). Part b (1.5 marks in total): - 1 mark for explaining that the base address 120 is added to the value stored in the index register (1) to yield the target address of 121. - 0.5 marks for specifying that the value loaded into ACC is the data stored at this calculated address (22).
Question 10 · Short Answer & Trace
7.5 marks
A circular queue is implemented in a 1D array Queue[0:4] of size 5. The queue pointers are FrontPointer (index of the front element), RearPointer (index of the rear element), and QueueLength (number of elements currently in the queue). (a) Given the initial state: Queue = ['A', 'B', 'C', '', ''], FrontPointer = 0, RearPointer = 2, and QueueLength = 3. Trace the contents of the queue and the pointers after the execution of the following sequential operations: 1. DEQUEUE, 2. DEQUEUE, 3. ENQUEUE('D'), 4. ENQUEUE('E'), 5. ENQUEUE('F'), 6. DEQUEUE. (b) Explain why a circular queue is more space-efficient than a linear queue when implemented using a static array.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Let's work through each step sequentially: - Initial: Queue[0]='A', Queue[1]='B', Queue[2]='C', Queue[3]='', Queue[4]=''. FrontPointer = 0, RearPointer = 2, QueueLength = 3. - Step 1 (DEQUEUE): The item at FrontPointer (Queue[0], 'A') is removed. FrontPointer incremented to (0+1) MOD 5 = 1. QueueLength decreases to 2. - Step 2 (DEQUEUE): The item at FrontPointer (Queue[1], 'B') is removed. FrontPointer incremented to (1+1) MOD 5 = 2. QueueLength decreases to 1. - Step 3 (ENQUEUE 'D'): RearPointer incremented to (2+1) MOD 5 = 3. Queue[3] becomes 'D'. QueueLength increases to 2. - Step 4 (ENQUEUE 'E'): RearPointer incremented to (3+1) MOD 5 = 4. Queue[4] becomes 'E'. QueueLength increases to 3. - Step 5 (ENQUEUE 'F'): RearPointer incremented to (4+1) MOD 5 = 0 (wrap-around!). Queue[0] becomes 'F'. QueueLength increases to 4. - Step 6 (DEQUEUE): The item at FrontPointer (Queue[2], 'C') is removed. FrontPointer incremented to (2+1) MOD 5 = 3. QueueLength decreases to 3.
Marking scheme
Part a (6 marks in total): - 1 mark for correct state after Step 1 (FrontPointer=1, QueueLength=2, 'A' removed). - 1 mark for correct state after Step 2 (FrontPointer=2, QueueLength=1, 'B' removed). - 1 mark for correct state after Step 3 (RearPointer=3, QueueLength=2, 'D' placed at index 3). - 1 mark for correct state after Step 4 (RearPointer=4, QueueLength=3, 'E' placed at index 4). - 1 mark for correct state after Step 5 showing circular wrap-around (RearPointer=0, QueueLength=4, 'F' placed at index 0). - 1 mark for correct state after Step 6 (FrontPointer=3, QueueLength=3, 'C' removed from index 2). Part b (1.5 marks in total): - 1 mark for explaining that a linear queue leaves empty, unusable slots at the front of the array after dequeuing. - 0.5 marks for explaining that wrap-around allows the circular queue to reuse these slots, optimizing memory usage.
Paper 21: AS Fundamental Problem-solving and Programming Skills
Answer all questions. Reference the insert for standard pseudocode functions and operators. Total marks: 75.
8 Question · 75 marks
Question 1 · Pseudocode Writing & Tracing
9.375 marks
Write a pseudocode function named `CalculateSpecialAverage` which takes a 1D array of 50 integers named `Values` as a parameter. The function must search through the array, calculate the mathematical average of all integers that are multiples of both 3 and 5, and return this average as a real number.
If the array contains no multiples of both 3 and 5, the function must return `-1.0`.
Ensure that you declare all of your variables and use standard pseudocode syntax.
Show answer & marking schemeHide answer & marking scheme
Worked solution
The algorithm iterates through all elements from index 1 to 50. In each step, it uses the modulo operator (`MOD`) to check if the current element is divisible by both 3 and 5. If it is, the element is accumulated in a running total (`Sum`), and a tally (`Count`) is incremented. Finally, to prevent division by zero, the count is checked. If it is greater than zero, the average is calculated and returned; otherwise, `-1.0` is returned.
Marking scheme
- 1 mark: Correct function header and return type (`RETURNS REAL`) - 1 mark: Explicit declaration of all local variables (`Index`, `Sum`, `Count`, `Average`) - 1 mark: Initialization of accumulator `Sum` and counter `Count` to 0 - 1 mark: Loop running correctly from 1 to 50 - 2 marks: Correct condition statement `(Values[Index] MOD 3 = 0) AND (Values[Index] MOD 5 = 0)` - 1 mark: Correct addition to `Sum` and increment of `Count` within the conditional block - 1.375 marks: Check `Count > 0` before calculating division to prevent runtime errors, and returning the correct average - 1 mark: Correctly returning `-1.0` if no matches are found
Question 2 · Pseudocode Writing & Tracing
9.375 marks
Consider the following pseudocode function:
```pseudocode FUNCTION FormatString(InStr : STRING) RETURNS STRING DECLARE OutStr : STRING DECLARE Length : INTEGER DECLARE I : INTEGER
OutStr <- "" Length <- LENGTH(InStr)
FOR I <- Length DOWNTO 1 OutStr <- OutStr & SUBSTRING(InStr, I, 1) IF (Length - I + 1) MOD 2 = 0 AND I <> 1 THEN OutStr <- OutStr & "#" ENDIF ENDFOR
RETURN OutStr ENDFUNCTION ```
Trace the execution of this function when called as follows: `FormatString("CAMBRIDGE")`
Provide the step-by-step state of the local variables `I`, `Length - I + 1`, and `OutStr` for each iteration of the loop.
Show answer & marking schemeHide answer & marking scheme
Worked solution
The function reverses the string "CAMBRIDGE" (which has a length of 9) by counting down from 9 to 1. During each iteration, the single character at index `I` is concatenated to `OutStr`. If the position from the start of the reversed string, calculated as `Length - I + 1`, is an even number (i.e. divisible by 2) and it is not the last character being processed (`I <> 1`), a hash character `"#"` is also appended. Let's dry run the loops: - `I = 9`: appends "E". `Length - I + 1 = 1` (odd). `OutStr` becomes "E". - `I = 8`: appends "G". `Length - I + 1 = 2` (even). Appends "#". `OutStr` becomes "EG#". - `I = 7`: appends "D". `Length - I + 1 = 3` (odd). `OutStr` becomes "EG#D". - `I = 6`: appends "I". `Length - I + 1 = 4` (even). Appends "#". `OutStr` becomes "EG#DI#". - `I = 5`: appends "R". `Length - I + 1 = 5` (odd). `OutStr` becomes "EG#DI#R". - `I = 4`: appends "B". `Length - I + 1 = 6` (even). Appends "#". `OutStr` becomes "EG#DI#RB#". - `I = 3`: appends "M". `Length - I + 1 = 7` (odd). `OutStr` becomes "EG#DI#RB#M". - `I = 2`: appends "A". `Length - I + 1 = 8` (even). Appends "#". `OutStr` becomes "EG#DI#RB#MA#". - `I = 1`: appends "C". `Length - I + 1 = 9` (odd). `I = 1` prevents any extra symbol. `OutStr` becomes "EG#DI#RB#MA#C".
Marking scheme
- 2 marks: Correctly tracking loop variable `I` descending from 9 down to 1. - 2 marks: Correct calculation of `Length - I + 1` from 1 to 9. - 2 marks: Correct character extracted via `SUBSTRING` in reverse sequence (E, G, D, I, R, B, M, A, C). - 2.375 marks: Correct placement of '#' characters after even-indexed characters of output string, ensuring no trailing '#' at the end (after 'C') because of condition `I <> 1`.
Question 3 · Pseudocode Writing & Tracing
9.375 marks
A school stores exam results for 10 students across 5 different subjects in a 2D array defined as: `DECLARE Scores : ARRAY[1:10, 1:5] OF INTEGER`
Write a pseudocode procedure named `IdentifyTopStudent` which receives the `Scores` array as a parameter. The procedure must: 1. Calculate the total cumulative score (sum of all 5 subjects) for each student (rows 1 to 10). 2. Output the student index (the row number) of the student with the highest cumulative score. 3. If multiple students share the highest cumulative score, output the index of the first student who achieved that score.
Ensure that you declare all of your local variables.
Show answer & marking schemeHide answer & marking scheme
Worked solution
The procedure uses nested loops: the outer loop runs from `Row <- 1 TO 10` to inspect each student, and the inner loop runs from `Col <- 1 TO 5` to accumulate the total marks of the current student into `Total`. The condition `Total > MaxTotal` compares the cumulative marks of the current student to the highest score found so far. It updates `MaxTotal` and records the current row as the `BestStudent` only if the new total is strictly greater, preserving the first student's index in case of a tie.
Marking scheme
- 1 mark: Correct procedure declaration header with array argument typed correctly. - 1 mark: Proper declaration of all local variables (`Row`, `Col`, `Total`, `MaxTotal`, `BestStudent` as `INTEGER`). - 1 mark: Initializing `MaxTotal` to a value of -1 (or to the first student's sum) and `BestStudent` to 0. - 1.375 marks: Correct nested loop bounds (outer: `1 TO 10`, inner: `1 TO 5`). - 2 marks: Correctly resetting `Total` inside the outer loop, and accumulating elements `Scores[Row, Col]` inside the inner loop. - 2 marks: Correctly comparing `Total > MaxTotal` and updating `MaxTotal` and `BestStudent` inside the conditional block. - 1 mark: Correctly outputting the index after loop termination.
Question 4 · Pseudocode Writing & Tracing
9.375 marks
An inventory program uses a composite user-defined data type `StockItem` defined as follows:
```pseudocode TYPE StockItem DECLARE ID : STRING DECLARE Quantity : INTEGER DECLARE Price : REAL ENDTYPE ```
An array `Stock` of size 4 contains the following database values: - `Stock[1]`: ID = "A10", Quantity = 12, Price = 15.00 - `Stock[2]`: ID = "B22", Quantity = 4, Price = 45.00 - `Stock[3]`: ID = "C35", Quantity = 25, Price = 8.50 - `Stock[4]`: ID = "D40", Quantity = 1, Price = 120.00
The procedure `CheckValue` is executed:
```pseudocode PROCEDURE CheckValue(Stock : ARRAY OF StockItem) DECLARE Index : INTEGER DECLARE Value : REAL DECLARE HighValueCount : INTEGER
HighValueCount <- 0
FOR Index <- 1 TO 4 Value <- Stock[Index].Quantity * Stock[Index].Price IF Value >= 100.00 THEN HighValueCount <- HighValueCount + 1 OUTPUT Stock[Index].ID, " is high value: ", Value ELSE OUTPUT Stock[Index].ID, " is standard: ", Value ENDIF ENDFOR OUTPUT "Total High Value Items: ", HighValueCount ENDPROCEDURE ```
Complete the trace table showing the changes of the variables `Index` and `Value`, along with any final output streams produced.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Let's perform calculations step-by-step for each record: 1. Index 1: `Stock[1]` has `Quantity = 12`, `Price = 15.00`. Value = 12 * 15.00 = 180.00. Value >= 100.00 is true. HighValueCount increments to 1. Outputs "A10 is high value: 180.00". 2. Index 2: `Stock[2]` has `Quantity = 4`, `Price = 45.00`. Value = 4 * 45.00 = 180.00. Value >= 100.00 is true. HighValueCount increments to 2. Outputs "B22 is high value: 180.00". 3. Index 3: `Stock[3]` has `Quantity = 25`, `Price = 8.50`. Value = 25 * 8.50 = 212.50. Value >= 100.00 is true. HighValueCount increments to 3. Outputs "C35 is high value: 212.50". 4. Index 4: `Stock[4]` has `Quantity = 1`, `Price = 120.00`. Value = 1 * 120.00 = 120.00. Value >= 100.00 is true. HighValueCount increments to 4. Outputs "D40 is high value: 120.00". At the end of the loop, outputs "Total High Value Items: 4".
Marking scheme
- 2 marks: Correct calculation of `Value` for all four iterations (180.00, 180.00, 212.50, 120.00). - 2 marks: Correctly determining that all four items are high value (value >= 100.00). - 3 marks: Showing the output lines produced from the conditions inside the loop. - 2.375 marks: Outputting the final print statement with the correct high value item count of 4.
Question 5 · Pseudocode Writing & Tracing
9.375 marks
A program validates user accounts. Write a pseudocode function named `ValidateUsername` that takes a single parameter `Username` of type `STRING` and returns a `BOOLEAN` value.
To be considered valid, the username must meet the following three rules: 1. The length of the string must be between 6 and 15 characters inclusive. 2. The first character of the string must be a letter (lowercase 'a' to 'z' or uppercase 'A' to 'Z'). 3. The string must contain at least one digit character ('0' to '9').
You should assume standard pseudocode string functions like `LENGTH(s)`, `SUBSTRING(s, start, length)` and `LCASE(s)` are available.
Show answer & marking schemeHide answer & marking scheme
Worked solution
The function performs three sequential checks. First, it extracts the length and rejects values outside range `[6, 15]`. Next, it converts the first character to lowercase using `LCASE` and verifies if it falls between ASCII values of `'a'` and `'z'`. Finally, it traverses the entire username string to look for any character between `'0'` and `'9'`. If a digit is found, the flag `HasDigit` is set to `TRUE`. The function returns `TRUE` if all conditions are satisfied.
Marking scheme
- 1 mark: Correct function declaration and parameter list returning `BOOLEAN`. - 1 mark: Declaration of local variables with correct data types. - 1.375 marks: Correct length check (`LENGTH(Username) >= 6 AND LENGTH(Username) <= 15`). - 2 marks: Correct logic to extract the first character and check if it is within character range 'a'-'z' or 'A'-'Z'. - 2 marks: Loop through string from 1 to `Length` checking if any character is in range '0'-'9', setting a flag. - 2 marks: Returning `TRUE` if all checks pass, and returning `FALSE` otherwise.
Question 6 · Pseudocode Writing & Tracing
9.375 marks
Consider the following pseudocode implementing a bubble sort algorithm to sort an array of five elements in ascending order:
```pseudocode PROCEDURE SortArray(BYREF Arr : ARRAY OF INTEGER) DECLARE Outer, Inner, Temp : INTEGER FOR Outer <- 1 TO 4 FOR Inner <- 1 TO 5 - Outer IF Arr[Inner] > Arr[Inner + 1] THEN Temp <- Arr[Inner] Arr[Inner] <- Arr[Inner + 1] Arr[Inner + 1] <- Temp ENDIF ENDFOR ENDFOR ENDPROCEDURE ```
The initial array contains the following sequence: `[25, 12, 45, 8, 30]`
Dry run this algorithm and trace the exact state of the array `Arr` at the end of each iteration of the `Outer` loop (i.e., when `Outer = 1`, `Outer = 2`, `Outer = 3`, and `Outer = 4`).
Show answer & marking schemeHide answer & marking scheme
Worked solution
Let us trace the passes: - Initial sequence: `[25, 12, 45, 8, 30]` - **Outer = 1** (Inner loops from 1 to 4): - Inner = 1: Compare 25 and 12 -> Swap. Array: `[12, 25, 45, 8, 30]` - Inner = 2: Compare 25 and 45 -> No swap. Array: `[12, 25, 45, 8, 30]` - Inner = 3: Compare 45 and 8 -> Swap. Array: `[12, 25, 8, 45, 30]` - Inner = 4: Compare 45 and 30 -> Swap. Array: `[12, 25, 8, 30, 45]` - **Outer = 2** (Inner loops from 1 to 3): - Inner = 1: Compare 12 and 25 -> No swap. - Inner = 2: Compare 25 and 8 -> Swap. Array: `[12, 8, 25, 30, 45]` - Inner = 3: Compare 25 and 30 -> No swap. - **Outer = 3** (Inner loops from 1 to 2): - Inner = 1: Compare 12 and 8 -> Swap. Array: `[8, 12, 25, 30, 45]` - Inner = 2: Compare 12 and 25 -> No swap. - **Outer = 4** (Inner loops 1 to 1): - Inner = 1: Compare 8 and 12 -> No swap. Array remains `[8, 12, 25, 30, 45]`.
Marking scheme
- 2 marks: Correct state after `Outer = 1` (`[12, 25, 8, 30, 45]`). - 2.375 marks: Correct state after `Outer = 2` (`[12, 8, 25, 30, 45]`). - 2.5 marks: Correct state after `Outer = 3` (`[8, 12, 25, 30, 45]`). - 2.5 marks: Correct state after `Outer = 4` (`[8, 12, 25, 30, 45]`).
Question 7 · Pseudocode Writing & Tracing
9.375 marks
Write a pseudocode procedure named `ProcessGrades` that manipulates student files. The procedure must perform the following actions: 1. Open an existing text file `Grades.txt` for sequential reading. 2. Open or create a text file named `HighAchievers.txt` for sequential writing. 3. Read data from `Grades.txt` which is structured sequentially in alternating pairs of lines: - Line A: Student Name (STRING) - Line B: Student Score (INTEGER) 4. For each student record, if the score is greater than or equal to 85, write the student's name and score as two separate lines into `HighAchievers.txt`. 5. Keep track of both the total number of students processed and the total number of high achievers exported. 6. Close both files and print the counts in the format: "Processed: High: ".
Show answer & marking schemeHide answer & marking scheme
Worked solution
The procedure establishes file handles with `OPENFILE`. It utilizes a `WHILE NOT EOF` control structure to sequentially fetch data from the base file in alternating steps (first the name, then the score). For each record, it increments the general counter. If the score condition `Score >= 85` is met, it formats and transfers the values to the secondary file and increments the high-score counter. Finally, file streams are cleanly closed using `CLOSEFILE` to prevent file corruption, and total counts are printed.
Marking scheme
- 1 mark: Correct syntax for procedure header and local variable declarations. - 1 mark: Initialization of accumulator variables (`TotalProcessed`, `HighCount`) to 0. - 2 marks: Correct file opening modes (`OPENFILE "Grades.txt" FOR READ`, `OPENFILE "HighAchievers.txt" FOR WRITE`). - 1.375 marks: Proper file scanning loop `WHILE NOT EOF("Grades.txt") DO`. - 2 marks: Accurate alternating reads from file using `READFILE` into distinct variables and incrementing processed count. - 1 mark: Logic check `Score >= 85` and writes to output file using `WRITEFILE`. - 1 mark: Closing of both files via `CLOSEFILE` and formatting of output strings.
Question 8 · Pseudocode Writing & Tracing
9.375 marks
Consider the following pseudocode algorithm designed to compute an integer checksum:
```pseudocode FUNCTION GenerateChecksum(Num : INTEGER) RETURNS INTEGER DECLARE Total, Temp, Digit, Weight : INTEGER Total <- 0 Temp <- Num Weight <- 1
WHILE Temp > 0 DO Digit <- Temp MOD 10 Total <- Total + (Digit * Weight) Temp <- Temp DIV 10 Weight <- Weight + 1 ENDWHILE
RETURN Total MOD 11 ENDFUNCTION ```
Dry run this function with the input argument `Num = 5824`. Identify and record the values of the variables `Temp`, `Digit`, `Total`, and `Weight` at the end of each iteration of the loop, then state the final returned value.
Show answer & marking schemeHide answer & marking scheme
The loop exits because `Temp > 0` (0 > 0) is false.
Now, return value: `Total MOD 11 = 52 MOD 11`. Since \(11 \times 4 = 44\), the remainder is \(52 - 44 = 8\). Therefore, the returned checksum value is `8`.
Marking scheme
- 2 marks: Correct tracking of digit isolation (`Digit`) and division (`Temp`) in all iterations. - 2 marks: Correct incremental change of `Weight` from 1 to 5. - 3 marks: Correct updates of `Total` at each step (4, 8, 32, 52). - 2.375 marks: Accurately calculating final checksum modulo division: `52 MOD 11` which yields the value `8`.
Paper 31: A Level Advanced Theory
Answer all questions. Calculators must not be used. Total marks: 75.
11 Question · 75.39999999999999 marks
Question 1 · Advanced Theory & Calculations
6.8 marks
A real number is stored in a 12-bit floating-point representation, with an 8-bit mantissa and a 4-bit exponent. Both mantissa and exponent are in two's complement form. Calculate the denary value represented by the following 12-bit binary pattern: 100100000011. Show all your steps.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. Identify the components: Mantissa = 10010000 (8 bits), Exponent = 0011 (4 bits). 2. Interpret the exponent: 0011 in denary is +3. 3. Interpret the mantissa: The binary point is after the first bit, so it represents the binary value 1.0010000. 4. Shift the binary point: Since the exponent is +3, shift the point 3 places to the right, yielding 1001.0000. 5. Convert to denary: Since the MSB is 1, it is a negative number in two's complement. Inverting the bits of 1001.0 gives 0110.1, and adding 1 to the least significant position gives 0111.0, which corresponds to 7 in denary. Therefore, the final value is -7.0 (or -7).
Marking scheme
1 mark for identifying the exponent as +3. 2 marks for shifting the binary point correctly to get 1001.0 (or showing the intermediate mantissa value as -0.875). 2.8 marks for applying two's complement steps correctly to convert 1001.0 to denary. 1 mark for the correct final answer of -7 or -7.0.
Trace the steps the Prolog interpreter takes when resolving the query: ancestor(albert, emily). Identify which rules are evaluated and how backtracking occurs.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. The query matches ancestor(albert, emily). Rule 1 is tried first: parent(albert, emily). This fails as it is not in the database. 2. Rule 2 is tried: parent(albert, Z), ancestor(Z, emily). 3. The interpreter searches the database and finds parent(albert, bob), binding Z = bob. 4. It recursively queries ancestor(bob, emily). 5. For ancestor(bob, emily), Rule 1 is tried: parent(bob, emily). This fails. 6. Rule 2 is tried: parent(bob, Z1), ancestor(Z1, emily). It finds parent(bob, david), binding Z1 = david. It queries ancestor(david, emily). 7. For ancestor(david, emily), Rule 1 (parent(david, emily)) fails, and Rule 2 fails because david has no children in the database. 8. The interpreter backtracks to Z = bob, but there are no other children for bob. 9. It backtracks further to the original parent(albert, Z) and finds the next match: parent(albert, charlie), binding Z = charlie. 10. It recursively queries ancestor(charlie, emily). 11. Rule 1 is tried: parent(charlie, emily). This is a direct match in the database, which evaluates to True. 12. The interpreter successfully returns True.
Marking scheme
2 marks for tracing the first branch where Z is bound to bob and showing that it ultimately fails and backtracks. 2 marks for demonstrating backtracking to the original goal and binding Z to charlie. 2 marks for showing that the query ancestor(charlie, emily) matches parent(charlie, emily) directly. 0.8 marks for concluding with the final answer of True.
Question 3 · Advanced Theory & Calculations
6.8 marks
A virtual machine compiles high-level language source code into intermediate bytecode, which is then executed by an interpreter. Explain two benefits and two limitations of running bytecode on a virtual machine compared to executing native machine code directly. Furthermore, explain how a Just-In-Time (JIT) compiler addresses one of these limitations.
Show answer & marking schemeHide answer & marking scheme
Worked solution
Benefits: 1. Platform Independence: The bytecode can run on any physical computer architecture, provided there is a virtual machine designed for that specific system. 2. Sandboxing/Security: The virtual machine acts as an intermediary layer, preventing faulty or malicious bytecode from executing direct hardware commands or corrupting memory. Limitations: 1. Execution Speed: Interpreting bytecode line-by-line is slower than executing native machine code compiled directly for the host processor. 2. Resource Overhead: The virtual machine requires its own memory allocation and processing time to interpret the bytecode. JIT Mitigation: A JIT compiler compiles frequently executed segments of bytecode (hotspots) directly into machine code at runtime, allowing them to run at native speeds and resolving the execution speed limitation.
Marking scheme
2 marks for two clearly explained benefits (platform independence and security sandboxing). 2 marks for two clearly explained limitations (performance overhead and memory usage). 2 marks for explaining that JIT compilation converts hot bytecode to native machine code at runtime. 0.8 marks for specifying that JIT addresses the execution speed limitation.
Question 4 · Advanced Theory & Calculations
6.8 marks
An artificial neural network node (neuron) has three inputs: \( x_1 = 0.8 \), \( x_2 = 0.5 \), and \( x_3 = 0.2 \). The respective weights associated with these inputs are \( w_1 = 0.5 \), \( w_2 = -0.4 \), and \( w_3 = 1.5 \). The bias is \( b = 0.2 \). Calculate the output of this node if it uses a Rectified Linear Unit (ReLU) activation function. Show all your intermediate calculations.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. Calculate the weighted sum of inputs (S): \( S = (x_1 \times w_1) + (x_2 \times w_2) + (x_3 \times w_3) + b \). 2. Substitute the values: \( S = (0.8 \times 0.5) + (0.5 \times -0.4) + (0.2 \times 1.5) + 0.2 \). 3. Perform multiplications: \( S = 0.4 - 0.2 + 0.3 + 0.2 \). 4. Calculate final sum: \( S = 0.7 \). 5. Apply the ReLU activation function: \( f(S) = \max(0, S) \). Since \( S = 0.7 \) is positive, the output is \( 0.7 \).
Marking scheme
2 marks for setting up the correct weighted sum equation including the bias. 2 marks for correct intermediate arithmetic of multiplications and final sum. 2 marks for correctly defining and applying the ReLU function to the sum. 0.8 marks for the correct final answer of 0.7.
Question 5 · Advanced Theory & Calculations
6.8 marks
The following Backus-Naur Form (BNF) rules define a simple mathematical expression grammar:
::= 2 | 3 | 4 ::= + | * ::= | ( ) ::= |
Determine whether the string '2 * (3 + 4)' is valid according to this grammar. Show the step-by-step derivation sequence starting from .
Show answer & marking schemeHide answer & marking scheme
Worked solution
To determine validity, we derive the target string '2 * (3 + 4)' from : 1. Start with . Apply the rule ::= . 2. Replace with , then 2. (String: '2 ') 3. Replace with '*'. (String: '2 * ') 4. For the remaining , apply the rule ::= . 5. Replace with '( )'. (String: '2 * ( )') 6. For the inner , apply ::= . 7. Replace inner with , then 3. (String: '2 * ( 3 )') 8. Replace inner with '+'. (String: '2 * ( 3 + )') 9. For the remaining inner , apply ::= , then ::= , then 4. (String: '2 * ( 3 + 4 )') Since all components match the target string, the expression is valid.
Marking scheme
2 marks for correctly splitting the outer expression into term, op, and expression. 2 marks for parsing the parenthesized sub-expression correctly as a term containing an inner expression. 2 marks for fully resolving the inner expression '3 + 4' to digits and operations. 0.8 marks for concluding that the string is valid.
Question 6 · Advanced Theory & Calculations
6.8 marks
Analyze the time complexity of the following pseudocode algorithm in terms of Big O notation:
total <- 0 FOR i <- 1 TO n j <- 1 WHILE j < n total <- total + (i * j) j <- j * 2 ENDWHILE ENDFOR
Show your analysis of the outer and inner loops separately, and state the final overall complexity.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. Outer Loop Analysis: The outer loop 'FOR i <- 1 TO n' executes exactly n times, where n is the input size. Thus, it has a linear time complexity of O(n). 2. Inner Loop Analysis: The inner loop is controlled by the variable j, which starts at 1 and doubles in value during each iteration (j <- j * 2) until it reaches or exceeds n. The sequence of values for j is 1, 2, 4, 8, ..., up to n. The number of iterations required for j to reach n is determined by the equation 2^k = n, which yields k = log_2(n) iterations. Therefore, the inner loop has a logarithmic time complexity of O(log n). 3. Overall Complexity: Since the inner loop is nested inside the outer loop, we multiply their individual complexities: O(n) * O(log n) = O(n log n).
Marking scheme
2 marks for analyzing the outer loop as O(n). 2 marks for demonstrating that the inner loop doubles the variable j, leading to log_2(n) iterations (O(log n)). 2 marks for explaining that the nested nature means multiplying the complexities. 0.8 marks for the correct final Big O notation.
Question 7 · Advanced Theory & Calculations
6.8 marks
Explain how asymmetric encryption, a digital certificate, and symmetric encryption work together during the SSL/TLS handshake protocol to establish a secure communication session between a client web browser and a server.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. Handshake Initiation: The client sends a connection request to the server, specifying supported cryptographic algorithms. 2. Certificate and Public Key Transmission: The server responds by sending its digital certificate, which contains the server's public key and is signed by a trusted Certificate Authority (CA). 3. Verification: The client verifies the authenticity of the digital certificate using the pre-installed public key of the CA. 4. Key Exchange (Asymmetric): The client generates a random symmetric session key. It encrypts this session key using the server's public key (retrieved from the digital certificate) and sends it to the server. 5. Decryption: The server decrypts the session key using its private key. 6. Secure Session (Symmetric): Both parties now possess the identical symmetric session key and use it to encrypt and decrypt all subsequent data transmitted during the session, benefiting from the faster speed of symmetric encryption.
Marking scheme
2 marks for explaining the role of the digital certificate and the verification of the server's public key via a trusted CA. 2 marks for explaining that asymmetric encryption is used to secure the transmission of a generated symmetric key. 2 marks for showing that the private key is used by the server to decrypt this symmetric key. 0.8 marks for explaining that subsequent communication uses symmetric encryption because it is computationally faster.
Question 8 · Advanced Theory & Calculations
6.8 marks
A network router receives a packet with the destination IP address 192.168.4.75. The router's routing table contains the following entries:
1) 192.168.4.0/24 via Interface A 2) 192.168.4.64/26 via Interface B 3) 192.168.0.0/16 via Interface C 4) 0.0.0.0/0 via Interface D
Determine which interface the router will forward the packet to. Show your calculations for each matching entry using binary prefix comparison, and explain how the router makes its final decision.
Show answer & marking schemeHide answer & marking scheme
Worked solution
1. Destination IP conversion: 192.168.4.75. The last octet (75) in binary is 01001011. 2. Evaluate Entry 1 (192.168.4.0/24): A /24 mask means the first 24 bits must match. 192.168.4 matches 192.168.4. This is a MATCH (Prefix length = 24). 3. Evaluate Entry 2 (192.168.4.64/26): A /26 mask means the first 26 bits must match. The first 24 bits match. For the remaining 2 bits of the last octet: The subnet prefix 64 in binary is 01000000. The destination octet 75 in binary is 01001011. The first 2 bits of both are '01'. Thus, they match. This is a MATCH (Prefix length = 26). 4. Evaluate Entry 3 (192.168.0.0/16): A /16 mask means the first 16 bits (192.168) must match. This is a MATCH (Prefix length = 16). 5. Evaluate Entry 4 (0.0.0.0/0): Default route, matches any address. This is a MATCH (Prefix length = 0). 6. Decision: The router applies the Longest Prefix Match rule. Comparing matching prefixes (26 > 24 > 16 > 0), the longest match is Entry 2 (/26). Therefore, the packet is forwarded via Interface B.
Marking scheme
2 marks for evaluating Entry 1 (/24) and Entry 3 (/16) matches correctly. 2 marks for converting the last octet of the destination IP (75) to binary (01001011) and comparing it to the /26 subnet boundary (64 -> 01000000) to prove Entry 2 matches. 2 marks for explaining the 'longest prefix match' rule. 0.8 marks for correctly naming Interface B as the final interface.
Question 9 · Advanced Theory & Calculations
7 marks
A computer system uses a 12-bit normalized floating-point representation. The mantissa is 8 bits and the exponent is 4 bits. Both fields use two's complement representation. (a) Represent the denary number \(-3.75\) in this format. Show your working. (b) Calculate the maximum positive denary value and the minimum negative denary value that can be represented using this system.
Show answer & marking schemeHide answer & marking scheme
Worked solution
For part (a): 1. Write the denary value as a sum of powers of 2. \(-3.75 = -4 + 0.25 = -2^2 + 2^{-2}\). In fixed-point binary (with the point after the MSB), \(-3.75\) can be represented as \(1100.0100_2\) where weights are \(-8, 4, 2, 1\) for integer bits and \(0.5, 0.25, 0.125, 0.0625\) for fractional bits. 2. To normalize this negative number, the binary point is shifted so the representation begins with the bits '1.0'. Shifting the point 2 places to the left gives \(1.0001000_2\) with an exponent of \(+2\). 3. Convert the exponent to 4-bit two's complement: \(+2 = 0010_2\). 4. Therefore, the mantissa is 10001000 and the exponent is 0010. For part (b): 1. Maximum positive value: The largest normalized positive mantissa is \(0.1111111_2\) which represents \(1 - 2^{-7} = 127/128 = 0.9921875\). The largest exponent is \(0111_2\) which represents \(+7\). Max value = \(0.9921875 \times 2^7 = 127\). 2. Minimum negative value: The most negative normalized mantissa is \(1.0000000_2\) which represents \(-1\). The largest exponent is \(0111_2\) which represents \(+7\). Min value = \(-1 \times 2^7 = -128\).
Marking scheme
Part (a) [4 marks]: 1 mark for converting -3.75 to binary fixed-point (e.g. 1100.0100). 1 mark for shifting the binary point to normalize (mantissa starts with 1.0). 1 mark for correct 8-bit normalized mantissa (10001000). 1 mark for correct 4-bit exponent (0010). Part (b) [3 marks]: 1 mark for identifying correct binary representation of max positive mantissa and exponent. 1 mark for calculating correct max positive value (127). 1 mark for calculating correct min negative value (-128).
Question 10 · Advanced Theory & Calculations
7 marks
A virtual memory system uses a 16-bit virtual address space. The page size is 4 KiB (\(4096\) bytes). The physical memory has a 20-bit address space. The page table contains the following active mappings: Page 0 maps to Frame 0x0A, Page 1 maps to Frame 0x1F, Page 2 maps to Frame 0x03, and Page 3 maps to Frame 0x2C. (a) Determine the number of bits allocated to the page number and the page offset in a virtual address. (b) Translate the virtual address 0x2A15 to its corresponding physical address in hexadecimal. Show your working. (c) State what occurs if the CPU attempts to reference the virtual address 0x502F.
Show answer & marking schemeHide answer & marking scheme
Worked solution
For part (a): 1. Page size is 4 KiB, which is \(2^{12}\) bytes. This requires 12 bits for the offset. 2. The virtual address is 16 bits. Therefore, the page number is \(16 - 12 = 4\) bits. For part (b): 1. Convert the virtual address 0x2A15 into binary: 0010 1010 0001 0101. 2. Separate into 4-bit page number and 12-bit offset: Page number = 0010 (binary) = 2 (denary), Offset = 1010 0001 0101 (binary) = 0xA15 (hex). 3. Lookup Page 2 in the page table: Page 2 maps to Frame 0x03. 4. Convert Frame 0x03 to 8 bits (since physical address space is 20 bits and offset is 12 bits): 0000 0011. 5. Combine the frame bits and offset bits: 0000 0011 1010 0001 0101. 6. Convert to hexadecimal: 0x03A15. For part (c): 1. The page number for virtual address 0x502F is 5. 2. Page 5 is not mapped in the page table, which triggers a page fault / page fault interrupt.
Marking scheme
Part (a) [2 marks]: 1 mark for correct offset bits (12). 1 mark for correct page number bits (4). Part (b) [3 marks]: 1 mark for identifying the correct page number (2) and offset (0xA15) from the address. 1 mark for mapping Page 2 to Frame 0x03. 1 mark for combining to get correct physical address (0x03A15). Part (c) [2 marks]: 1 mark for identifying that Page 5 is not mapped. 1 mark for stating that this causes a page fault / page fault interrupt.
Question 11 · Advanced Theory & Calculations
7 marks
An educational institution is allocated the IPv4 address block \(192.168.45.0/26\). (a) Express the subnet mask of this network in dotted-decimal notation. (b) Calculate the maximum number of usable host IP addresses available in this subnet. Show your working. (c) Determine the broadcast address of this subnet. Show your working.
Show answer & marking schemeHide answer & marking scheme
Worked solution
For part (a): 1. A prefix of /26 means the first 26 bits of the 32-bit subnet mask are set to 1, and the remaining 6 bits are set to 0. 2. Subnet mask in binary: 11111111.11111111.11111111.11000000. 3. Convert to dotted-decimal notation: 255.255.255.192. For part (b): 1. Number of host bits (n) = \(32 - 26 = 6\). 2. Total IP addresses in the subnet = \(2^n = 2^6 = 64\). 3. Usable host addresses = \(2^6 - 2 = 62\) (subtracting the network address and the broadcast address). For part (c): 1. The network address is 192.168.45.0. 2. To find the broadcast address, set all 6 host bits (the last 6 bits of the last octet) to 1. 3. Last octet binary: 00111111, which equals 63 in denary. 4. Therefore, the broadcast address is 192.168.45.63.
Marking scheme
Part (a) [2 marks]: 1 mark for identifying the binary mask structure. 1 mark for correct dotted-decimal conversion (255.255.255.192). Part (b) [2 marks]: 1 mark for identifying 6 host bits and calculating 64 total addresses. 1 mark for subtracting 2 to get 62 usable hosts. Part (c) [3 marks]: 1 mark for identifying that host bits must be set to 1 for the broadcast address. 1 mark for showing correct binary/decimal conversion of the last octet. 1 mark for correct broadcast address (192.168.45.63).
Paper 41: A Level Practical
Perform practical programming tasks using Java, Python, or Visual Basic. Code must be saved and evidence documents structured with screenshots. Total marks: 75.
3 Question · 75 marks
Question 1 · Practical
25 marks
An sports complex needs a program to manage court bookings. You are required to design and implement an object-oriented program to manage these bookings using an array.
1. Define a class `Booking` with the following private attributes: - `BookingID` (String) - `CustomerName` (String) - `Duration` (Integer, representing the number of hours booked) - `IsPeak` (Boolean, representing whether the booking is during peak hours)
2. Write a constructor for the `Booking` class that initialises all attributes from values passed as parameters.
3. Write getter methods for `BookingID` and `CustomerName`.
4. Write a method `CalculateFee()` that calculates and returns the cost of the booking (as an integer) based on the following rules: - The base rate is $15 per hour. - If the booking is during peak hours (`IsPeak` is True), an additional flat surcharge of $10 is added to the total.
5. Write a main program that does the following: - Declares a 1D array or list `BookingsList` capable of holding up to 20 `Booking` objects. - Reads booking data from a text file named `bookings.txt`. Each line in the file contains the booking ID, customer name, duration, and peak status (represented as "True" or "False") separated by commas. For example: `B001,John Doe,2,True` - Instantiates a `Booking` object for each line read and stores it in the `BookingsList` array. - Handles file-not-found exceptions with a friendly message.
6. Write a procedure `SearchHighValueBookings()` in your main program that iterates through `BookingsList` and prints the booking ID, customer name, and total fee for all bookings where the fee is strictly greater than $30.
Show answer & marking schemeHide answer & marking scheme
Worked solution
### Python Source Code
```python class Booking: def __init__(self, booking_id, customer_name, duration, is_peak): self.__booking_id = booking_id self.__customer_name = customer_name self.__duration = int(duration) # Convert string representation of boolean to actual boolean if isinstance(is_peak, str): self.__is_peak = is_peak.strip().lower() == "true" else: self.__is_peak = bool(is_peak)
def read_file(): global bookings_list try: with open("bookings.txt", "r") as file: for line in file: line = line.strip() if line: parts = line.split(",") if len(parts) == 4: b_id = parts[0] name = parts[1] duration = int(parts[2]) is_peak = parts[3] new_booking = Booking(b_id, name, duration, is_peak) bookings_list.append(new_booking) except FileNotFoundError: print("Error: bookings.txt file was not found.")
def search_high_value_bookings(): print("--- Bookings with fees over $30 ---") found = False for booking in bookings_list: fee = booking.calculate_fee() if fee > 30: print(f"ID: {booking.get_booking_id()} | Name: {booking.get_customer_name()} | Fee: ${fee}") found = True if not found: print("No bookings found with a fee greater than $30.")
- Class Definition & Attributes (4 marks): * Defining private/encapsulated fields `__booking_id`, `__customer_name`, `__duration`, `__is_peak` (or equivalent language structure). - Constructor & Getters (4 marks): * Correct parameters in constructor and assignments. * Getters return the correct attributes. - CalculateFee Method (4 marks): * Calculates base hourly rate (duration * 15). * Adds peak-hour flat rate surcharge ($10) conditional on boolean state. - File Handling & Parsing (6 marks): * Use of try-except block to handle missing file exception. * Correct splitting of values by comma and parsing string to Boolean/Integer. * Instantiation of `Booking` and insertion into list/array. - Search & Display Logic (5 marks): * Iterating through the array/list. * Calling `calculate_fee()` and filtering records where fee > 30. * Printing correctly formatted ID, Name, and Fee details. - Evidence & Output Verification (2 marks): * Screenshot showing successful compiler run with correct output.
Question 2 · Practical
25 marks
A binary search tree is to be implemented using a 1D array of node records to store student records (Student ID as integer, Student Name as string).
1. Define a class or record structure `Node` with the following fields: - `StudentID` (Integer) - `StudentName` (String) - `LeftPointer` (Integer) - `RightPointer` (Integer)
2. Write a procedure `Initialise()` to initialise an array named `Tree` of size 10 elements: - Each array element must contain a `Node` object initialized with `StudentID` as -1, `StudentName` as "", `LeftPointer` containing the index of the next free node (acting as a free list chain), and `RightPointer` as -1. - Initialize global variables: `RootPointer` as -1, and `FreePointer` as 0. - The last element's `LeftPointer` must be set to -1.
3. Write a recursive procedure `InsertNode(CurrentPointer, NewID, NewName)` that handles traversing the tree to insert a new student record into the correct place based on their `StudentID`. Update pointers and manage the `FreePointer` chain accordingly.
4. Write a recursive procedure `InOrder(CurrentPointer)` that performs an in-order traversal of the tree, displaying the `StudentID` and `StudentName` of each active node.
5. Test your tree in the main program by: - Calling `Initialise()` - Inserting five nodes with the following IDs and Names: * `105, "Alice"` * `102, "Bob"` * `108, "Charlie"` * `101, "David"` * `106, "Eve"` - Calling `InOrder(RootPointer)` and capturing the output screenshot.
Show answer & marking schemeHide answer & marking scheme
# Global Tree Management Variables Tree = [Node() for _ in range(10)] RootPointer = -1 FreePointer = 0
def Initialise(): global Tree, RootPointer, FreePointer Tree = [Node(-1, "") for _ in range(10)] for i in range(9): Tree[i].left_pointer = i + 1 Tree[9].left_pointer = -1 RootPointer = -1 FreePointer = 0
def InsertNode(new_id, new_name): global Tree, RootPointer, FreePointer if FreePointer == -1: print("Tree full: insertion aborted.") return
# Get next free node index insert_index = FreePointer FreePointer = Tree[FreePointer].left_pointer
if RootPointer == -1: RootPointer = insert_index else: current = RootPointer placed = False while not placed: if new_id < Tree[current].student_id: if Tree[current].left_pointer == -1: Tree[current].left_pointer = insert_index placed = True else: current = Tree[current].left_pointer else: if Tree[current].right_pointer == -1: Tree[current].right_pointer = insert_index placed = True else: current = Tree[current].right_pointer
def InOrder(current_pointer): global Tree if current_pointer != -1: InOrder(Tree[current_pointer].left_pointer) print(f"Student ID: {Tree[current_pointer].student_id}, Name: {Tree[current_pointer].student_name}") InOrder(Tree[current_pointer].right_pointer)
- Node Class (3 marks): * Declaring variables `StudentID`, `StudentName`, `LeftPointer`, `RightPointer` in class initialization. - Initialise Procedure (5 marks): * Array initialized with empty/dummy values. * Setting sequential linked pointers through `left_pointer` for the free pool list. * Setting the final element's pointer to -1. * Correct assignment of global variables `RootPointer` and `FreePointer`. - InsertNode Mechanism (8 marks): * Verifying space availability using `FreePointer` != -1. * Copying properties and resetting pointers to -1. * Traversing recursive/looping structure checking if value goes left vs right. * Updating structural left/right index pointers properly. - InOrder Traversal Procedure (5 marks): * Recursive base check (pointer != -1). * Correct sequence: left child traversal, print self node, right child traversal. - Test Execution & Verification (4 marks): * Executing program output successfully showing sorted student list from 101 to 108. * Screenshots showcasing tree execution.
Question 3 · Practical
25 marks
A process controller requires a Circular Queue data structure to manage printer jobs. The size of the queue is limited to 5 elements.
1. Define a class `Job` containing: - Private attribute `JobID` (String) - Private attribute `Priority` (Integer) - A constructor to initialisze these fields, and public getters for both fields.
2. Create a global array/list of size 5 named `JobQueue`, and global variables `Head` (initialised to -1), `Tail` (initialised to -1), and `NumElements` (initialised to 0).
3. Write an `Enqueue(NewJob)` function that: - Checks if the queue is full (displays an error and returns `False` if so). - Correctly updates the `Tail` pointer taking wrap-around into account. - Sets the value of `Head` to 0 if it is the very first element inserted. - Adds the `NewJob` object to the position pointed to by `Tail` and increments `NumElements`. - Returns `True` if successfully queued.
4. Write a `Dequeue()` function that: - Checks if the queue is empty (displays an error and returns `None` if so). - Extracts the `Job` at the index pointed to by `Head`. - Clears that slot in `JobQueue` by setting it to `None`. - Decrements `NumElements`. - Updates the `Head` pointer with wrap-around. - Resets both `Head` and `Tail` back to -1 if the queue is now empty. - Returns the extracted `Job` object.
5. Test the queue system in the main program by implementing these operations: - Create 4 Job objects: `(J01, 3)`, `(J02, 1)`, `(J03, 5)`, `(J04, 2)`. - Enqueue all 4 items sequentially, checking the return boolean. - Dequeue 2 items and print their ID and Priority. - Enqueue another 2 items: `(J05, 4)` and `(J06, 1)`. - Output the current active array contents showing the index, ID, and Priority of each spot (or indicating if the spot is empty).
Show answer & marking schemeHide answer & marking scheme
if NumElements == 0: Head = -1 Tail = -1 else: Head = (Head + 1) % 5
return removed_job
def PrintQueueState(): global JobQueue print(" --- Current Queue Array State ---") for i in range(5): item = JobQueue[i] if item is None: print(f"Index {i}: [EMPTY]") else: print(f"Index {i}: JobID: {item.get_job_id()}, Priority: {item.get_priority()}")
# Main Test Harness # 1. Enqueue 4 elements print("Enqueuing 4 jobs...") Enqueue(Job("J01", 3)) Enqueue(Job("J02", 1)) Enqueue(Job("J03", 5)) Enqueue(Job("J04", 2)) PrintQueueState()
# 2. Dequeue 2 elements print(" Dequeuing 2 jobs...") j1 = Dequeue() j2 = Dequeue() if j1: print(f"Dequeued: {j1.get_job_id()} with Priority {j1.get_priority()}") if j2: print(f"Dequeued: {j2.get_job_id()} with Priority {j2.get_priority()}") PrintQueueState()
# 3. Enqueue 2 more elements print(" Enqueuing 2 more jobs (J05, J06)...") Enqueue(Job("J05", 4)) Enqueue(Job("J06", 1)) PrintQueueState() ```
Marking scheme
- Job Class Definition (3 marks): * Defining private attributes correctly with constructor. * Defining public getter methods. - Queue Declaration (4 marks): * Array `JobQueue` defined with size 5. * Initial pointers `Head`, `Tail` set to -1, `NumElements` set to 0. - Enqueue Implementation (6 marks): * Overflow validation checks. * Wrap-around calculation logic using mod arithmetic: `(Tail + 1) % 5`. * Initializing pointer `Head` to 0 on initial insert. * Correct addition of elements and count update. - Dequeue Implementation (6 marks): * Underflow check validation. * Extracting job from index, deleting element from index state, decrementing count. * Handling reset conditions of `Head` and `Tail` to -1 if elements drop to 0. * Updating `Head` pointer using mod arithmetic. - Testing Execution & Verification (6 marks): * Implementation of sequence of actions outlined in step 5. * Verifying wrap-around behavior by showing index layout after modifications. * Capturing screenshot results.
Wondering how well you actually know this?
Thinka is an AI practice app for DSE 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.