Domain: Database Systems & Data Engineering

# Prompt

## Database Storage

### Problem Context and Objective

Consider a B+ tree where each node can hold a maximum of 4 keys and 5 pointers. The tree is initially empty. The following keys are inserted into the tree one at a time, in the order shown:

era, ban, bat, kin, day, log, rye, max, won, ace, ado, bug, cop, gas, let, fax

Use lexicographic (dictionary) ordering to compare strings.

**B+ tree insertion rules:**
- Keys are always inserted into the appropriate leaf node.
- When a leaf node overflows (receives a 5th key), sort all 5 keys, place the first 2 in the left node, place the last 3 in a new right node, and copy the first key of the right node up into the parent. If no parent exists, create a new root containing that single key.
- When an internal node overflows (receives a 5th key), sort all 5 keys, place the first 2 in the left node, place the last 2 in a new right node, and push the 3rd (middle) key up into the parent. If no parent exists, create a new root.
- In a leaf split, the promoted key is copied (it remains in the leaf). In an internal split, the promoted key is pushed up (it is removed from the child).

Compute the tree utilization after every 4 insertions. Utilization is defined as:

$$\text{utilization} = \frac{\text{total number of keys stored across all nodes}}{4 \times \text{total number of nodes}}$$

Store the utilization values as decimal fractions (between 0 and 1) in variables $a$, $b$, $c$, $d$ corresponding to after 4, 8, 12, and 16 insertions respectively. Round each to exactly 3 decimal places.

---

Now consider a separate problem. Assume we have a relation $r(X, Y)$ where the domain of $X$ is $\{A, B, C, D\}$ and the domain of $Y$ is integers. The hash function on $X$ is defined as follows (4-bit binary values):

| Key | Hash |
|-----|------|
| A   | 0100 |
| B   | 0101 |
| C   | 0011 |
| D   | 1101 |

The hash structure uses **prefix-based matching starting from the most significant bit (bit 1 is leftmost)**.

Construct an extendible hash structure by inserting the following records in the order shown:

(B,8), (D,4), (C,1), (A,7), (D,3), (D,9), (B,5)

Each bucket can hold at most 2 records.

**Extendible hashing rules:**
- Each bucket has a Local Depth (LD). The directory has a Global Depth (GD). Initially GD = 0 and there is one bucket with LD = 0.
- When a bucket overflows: if LD < GD, increment LD and split the bucket by examining the next bit of the hash. If LD = GD, first double the directory (increment GD), then increment LD and split.
- After a split, if all overflowing records still map to the same new bucket because their hash values share the same prefix for all bits up to the new LD, repeat the split process (doubling the directory again if needed).
- If all records in an overflowing bucket have identical full 4-bit hash values (so no further bit can separate them), stop splitting and attach an overflow page to that bucket instead.

In the final hash structure, number the distinct buckets 1 through $N$ in the order they are first encountered when scanning the directory from binary index 0000...0 to 1111...1.

---

**Computation:**

$$e = \text{round}(10 \times a \times b \times c \times d)$$

where $\text{round}$ denotes rounding to the nearest integer.

Let $N$ be the total number of distinct buckets (not counting overflow pages). Compute:
- Bucket index $p = ((e - 1) \bmod N)$. If $p = 0$, set $p = N$.
- Bucket index $q = ((e + 1) \bmod N)$. If $q = 0$, set $q = N$.

Count the records in buckets $p$ and $q$ (only records in the main bucket, not in overflow pages). Compute the fraction of total records (out of 7) stored in these two buckets combined. Round to 3 decimal places.

**Report the following values in your answer: $a$, $b$, $c$, $d$, $e$, and the final fraction.**

Grading rubric:
criterion: Does the solution report the utilization after 4 insertions as a = 1.000 (or equivalent rounding)?
weight: 10
criterion: Does the solution report the utilization after 8 insertions as b = 0.625?
weight: 10
criterion: Does the solution report the utilization after 12 insertions as c = 0.667?
weight: 10
criterion: Does the solution report the utilization after 16 insertions as d = 0.833?
weight: 10
criterion: Does the solution correctly compute the value of e as 3?
weight: 10
criterion: Does the solution determine that the final extendible hash structure has a global depth of 4?
weight: 15
criterion: Does the solution determine that the final extendible hash structure contains exactly 6 distinct buckets (not counting overflow pages)?
weight: 10
criterion: Does the solution correctly apply the overflow page rule for key D, recognizing that all D records share the identical 4-bit hash 1101 and therefore cannot be separated by further splitting?
weight: 15
criterion: Does the solution correctly determine the bucket indices p = 2 and q = 4 using modulo arithmetic with 1-based adjustment?
weight: 10
criterion: Does the solution correctly identify the contents of bucket 2 ([(A,7)]) and bucket 4 (empty), counting only main bucket records?
weight: 10
criterion: Does the solution output the final fraction as 0.143?
weight: 20

Answer:
# Golden Answer

## Part 1: B+ Tree Construction

The tree starts empty. Each node can hold at most 4 keys. The split rule for leaves: when a leaf has 5 keys, left gets keys at positions 1-2, right gets keys at positions 3-5, and the key at position 3 (first key of right node) is copied up to the parent.

**Insertions 1-4 (era, ban, bat, kin):**

Insert era: single leaf [era].
Insert ban: leaf [ban, era] (sorted).
Insert bat: leaf [ban, bat, era].
Insert kin: leaf [ban, bat, era, kin].

One node, 4 keys, 4 spots.
$a = 4/(4 \times 1) = 4/4 = 1.000$.

**Insertions 5-8 (day, log, rye, max):**

Insert day into leaf [ban, bat, era, kin]. Now has 5 keys: [ban, bat, day, era, kin].
Overflow: left = [ban, bat], right = [day, era, kin]. Copy "day" up to new root.
Tree: root=[day], leaves=[ban, bat] and [day, era, kin].

Insert log: goes to right leaf (log > day). Right leaf becomes [day, era, kin, log]. Fits (4 keys).

Insert rye: goes to right leaf. Now 5 keys: [day, era, kin, log, rye].
Overflow: left = [day, era], right = [kin, log, rye]. Copy "kin" up to root.
Root = [day, kin]. Leaves: [ban, bat], [day, era], [kin, log, rye].

Insert max: goes to rightmost leaf (max > kin). Leaf becomes [kin, log, max, rye]. Fits (4 keys).

Node count: 1 root + 3 leaves = 4.
Key count: root has 2, leaves have 2 + 2 + 4 = 8. Total = 10.
Spots: $4 \times 4 = 16$.
$b = 10/16 = 0.625$.

**Insertions 9-12 (won, ace, ado, bug):**

Insert won: goes to rightmost leaf [kin, log, max, rye]. Now 5 keys: [kin, log, max, rye, won].
Overflow: left = [kin, log], right = [max, rye, won]. Copy "max" up.
Root = [day, kin, max]. Leaves: [ban, bat], [day, era], [kin, log], [max, rye, won].

Insert ace: goes to leftmost leaf (ace < day, ace < ban). Leaf becomes [ace, ban, bat]. Fits.

Insert ado: goes to leftmost leaf. Leaf becomes [ace, ado, ban, bat]. Fits (4 keys, full).

Insert bug: goes to leftmost leaf (bug > ban, bug < day). Now 5 keys: [ace, ado, ban, bat, bug].
Overflow: left = [ace, ado], right = [ban, bat, bug]. Copy "ban" up.
Root = [ban, day, kin, max]. Leaves: [ace, ado], [ban, bat, bug], [day, era], [kin, log], [max, rye, won].

Node count: 1 root + 5 leaves = 6.
Key count: root has 4, leaves have 2 + 3 + 2 + 2 + 3 = 12. Total = 16.
Spots: $4 \times 6 = 24$.
$c = 16/24 = 0.66\overline{6} \approx 0.667$.

**Insertions 13-16 (cop, gas, let, fax):**

Insert cop: goes to leaf [ban, bat, bug] (cop > ban, cop < day). Leaf becomes [ban, bat, bug, cop]. Fits (full).

Insert gas: goes to leaf [day, era] (gas > day, gas < kin). Leaf becomes [day, era, gas]. Fits.

Insert let: goes to leaf [kin, log] (let > kin, let < log lexicographically). Leaf becomes [kin, let, log]. Fits.

Insert fax: goes to leaf [day, era, gas] (fax > day, fax < gas). Leaf becomes [day, era, fax, gas]. Fits (full).

Node count: 1 root + 5 leaves = 6. (No new splits occurred.)
Key count: root has 4, leaves have 2 + 4 + 4 + 3 + 3 = 16. Total = 20.
Spots: $4 \times 6 = 24$.
$d = 20/24 = 0.83\overline{3} \approx 0.833$.

**Summary:** $a = 1.000$, $b = 0.625$, $c = 0.667$, $d = 0.833$.

---

## Part 2: Extendible Hashing

Hash values (4-bit, MSB first): A = 0100, B = 0101, C = 0011, D = 1101.

**Step 1 - Initialization:** GD = 0. One bucket (LD = 0), empty.

**Step 2 - Insert (B,8):** Hash(B) = 0101. Bucket has room. Bucket: [(B,8)]. LD = 0.

**Step 3 - Insert (D,4):** Hash(D) = 1101. Same bucket (GD = 0, only one directory entry). Bucket: [(B,8), (D,4)]. Now full (capacity 2). LD = 0.

**Step 4 - Insert (C,1):** Hash(C) = 0011. Target bucket is full. Must split.
- LD = GD = 0, so double directory first. GD becomes 1. Directory now has 2 entries (0 and 1).
- Increment LD to 1. Split by bit 1:
  - B(0101) -> prefix 0 -> directory 0
  - C(0011) -> prefix 0 -> directory 0
  - D(1101) -> prefix 1 -> directory 1
- Directory 0 -> Bucket A (LD=1): [(B,8), (C,1)] - full
- Directory 1 -> Bucket B (LD=1): [(D,4)]

**Step 5 - Insert (A,7):** Hash(A) = 0100. Prefix "0" -> directory 0 -> Bucket A: [(B,8), (C,1)]. Full. Must split.
- LD = GD = 1, so double directory. GD becomes 2. Directory now has 4 entries (00, 01, 10, 11).
- Increment LD to 2. Split Bucket A by bit 2:
  - C(0011) -> prefix 00 -> directory 00
  - B(0101) -> prefix 01 -> directory 01
  - A(0100) -> prefix 01 -> directory 01
- Directory 00 -> Bucket C (LD=2): [(C,1)]
- Directory 01 -> Bucket D (LD=2): [(B,8), (A,7)] - full
- Directory 10 -> Bucket B (LD=1): [(D,4)] (unchanged, LD still 1)
- Directory 11 -> Bucket B (LD=1): [(D,4)] (same bucket as 10)

**Step 6 - Insert (D,3):** Hash(D) = 1101. Prefix "11" -> directory 11 -> Bucket B: [(D,4)]. Has room.
Bucket B: [(D,4), (D,3)]. Now full. LD = 1.

**Step 7 - Insert (D,9):** Hash(D) = 1101. Prefix "11" -> directory 11 -> Bucket B: [(D,4), (D,3)]. Full. Must split.
- LD(1) < GD(2), so no directory doubling needed. Increment LD to 2. Split by bit 2:
  - D(1101) -> prefix 11. All three D records (D,4), (D,3), (D,9) have hash 1101 -> prefix 11.
  - Directory 10 -> new Bucket E (LD=2): empty.
  - Directory 11 -> Bucket B (LD=2): All three D records still map here.
- Bucket B still has 3 records but capacity 2. Attempt further split:
  - LD(2) = GD(2), so must double directory to split further. But all three records have identical 4-bit hash 1101. Check: can bit 3 separate them? D's bit 3 is 0 for all. Can bit 4 separate them? D's bit 4 is 1 for all. Since all records have the identical full 4-bit hash value 1101, no further splitting can separate them.
  - Create overflow page.
- Directory 11 -> Bucket B (LD=2): [(D,4), (D,3)] + overflow page: [(D,9)]

State after step 7:
- 00 -> Bucket C (LD=2): [(C,1)]
- 01 -> Bucket D (LD=2): [(B,8), (A,7)]
- 10 -> Bucket E (LD=2): empty
- 11 -> Bucket B (LD=2): [(D,4), (D,3)] + overflow [(D,9)]

**Step 8 - Insert (B,5):** Hash(B) = 0101. Prefix "01" -> directory 01 -> Bucket D: [(B,8), (A,7)]. Full. Must split.
- LD(2) = GD(2), so double directory. GD becomes 3. Directory now has 8 entries (000-111).
- Increment LD to 3. Split Bucket D by bit 3:
  - A(0100) -> prefix 010
  - B(0101) -> prefix 010
  - B(0101) -> prefix 010 (the new record)
  - All three map to prefix 010. Prefix 011 is empty.
- Directory 010 -> Bucket D (LD=3): Still has A(0100), B(0101), B(0101) = 3 records. Still overflows.
- LD(3) = GD(3), so double directory again. GD becomes 4. Directory now has 16 entries (0000-1111).
- Increment LD to 4. Split Bucket D by bit 4:
  - A(0100) -> prefix 0100
  - B(0101) -> prefix 0101
  - B(0101) -> prefix 0101
  - Now separated: A goes to 0100, both B's go to 0101.
- Directory 0100 -> Bucket F (LD=4): [(A,7)]
- Directory 0101 -> Bucket D (LD=4): [(B,8), (B,5)]

**Final structure:** GD = 4, 16 directory entries.

Directory-to-bucket mapping (scanning 0000 to 1111, numbering new buckets in encounter order):

| Directory entries | Bucket | LD | Contents |
|---|---|---|---|
| 0000, 0001, 0010, 0011 | Bucket 1 | 2 | [(C,1)] |
| 0100 | Bucket 2 | 4 | [(A,7)] |
| 0101 | Bucket 3 | 4 | [(B,8), (B,5)] |
| 0110, 0111 | Bucket 4 | 3 | empty |
| 1000, 1001, 1010, 1011 | Bucket 5 | 2 | empty |
| 1100, 1101, 1110, 1111 | Bucket 6 | 2 | [(D,4), (D,3)] + overflow [(D,9)] |

Verification of directory entry counts per bucket:
- Bucket 1 (LD=2): $2^{4-2} = 4$ entries. Correct (0000-0011).
- Bucket 2 (LD=4): $2^{4-4} = 1$ entry. Correct (0100).
- Bucket 3 (LD=4): $2^{4-4} = 1$ entry. Correct (0101).
- Bucket 4 (LD=3): $2^{4-3} = 2$ entries. Correct (0110, 0111).
- Bucket 5 (LD=2): $2^{4-2} = 4$ entries. Correct (1000-1011).
- Bucket 6 (LD=2): $2^{4-2} = 4$ entries. Correct (1100-1111).
Total entries: 4+1+1+2+4+4 = 16. Correct.

$N = 6$ buckets. Total records = 7 (6 in main buckets + 1 in overflow).

---

## Final Computation

$e = \text{round}(10 \times 1.000 \times 0.625 \times 0.667 \times 0.833)$

$= \text{round}(10 \times 0.34722) = \text{round}(3.4722) = 3$

$p = ((e-1) \bmod N) = ((3-1) \bmod 6) = (2 \bmod 6) = 2$. Since $p \neq 0$, bucket $p = 2$.
$q = ((e+1) \bmod N) = ((3+1) \bmod 6) = (4 \bmod 6) = 4$. Since $q \neq 0$, bucket $q = 4$.

Bucket 2: [(A,7)] = 1 record.
Bucket 4: empty = 0 records.

Fraction $= (1 + 0)/7 = 1/7 = 0.14285... \approx 0.143$.

**Answer:** $a = 1.000$, $b = 0.625$, $c = 0.667$, $d = 0.833$, $e = 3$, final fraction = **0.143**.