Skip to lab content

WEEK 11 · ADVANCED DATA STRUCTURES LAB

Hash function — mid-square method

B.Tech CSE & Allied Branches · II Year, I Semester · AR25C173 tested sample runs

THE SYLLABUS TASK

What you need to solve

Implement the Mid-square Method hash function.
Input format & conventions

Nonnegative key (0–1,000,000), then table size m (1–1000). Extract decimal width = digits in m−1. If the square is shorter, use its whole value; an uneven margin keeps the earlier centered window. Reduce the extracted number modulo m.

UNDERSTAND THE IDEA

Explanation

Mid-square hashing first squares the key and then selects a central digit window. Central digits can reflect contributions from several positions of the original key.

The method needs an explicit window convention. Here its width matches the digits needed to represent m−1, and start = floor((squareDigits−width)/2).

The square is evaluated as long long before multiplication. A final modulo is necessary when the selected decimal number is not already below the requested table size.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Validate key and table size.
  2. Compute the square in long long and convert it to decimal digits.
  3. Determine the extraction width from m−1.
  4. Extract the centered window, or use the whole square if shorter.
  5. Reduce modulo m and print the index.

SEE THE CONTROL FLOW

Flowcharts

Diamonds show decisions; labeled arrows select a branch. Returning arrows repeat work. Helper-function steps group the operations explained above and in the code. These charts focus on the named core operation. On a phone, scroll within the diagram or open the full-size version.

Compute one hash index

Compute one hash index

Open full-size flowchart ↗

C17

Complete C program

Download .c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
    int ch; size_t used = 0; int fits = 1;
    while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
    if (ch == EOF) return 0;
    do {
        if (ch == 0) fits = 0;
        if (used + 1 < capacity) text[used++] = (char)ch;
        else fits = 0;
        ch = getchar();
    } while (ch != EOF && !isspace((unsigned char)ch));
    text[used] = '\0';
    return fits;
}
int readInt(int *value) {
    char token[64], *end;
    if (!readToken(token, sizeof token)) return 0;
    errno = 0; long result = strtol(token, &end, 10);
    if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;
    *value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }

int hash(int key, int size) {
    long long square = (long long)key * key;
    char digits[32]; snprintf(digits,sizeof digits,"%lld",square);
    int width = 1; for (int maxIndex = size - 1; maxIndex >= 10; maxIndex /= 10) ++width;
    int length = (int)strlen(digits);
    if (length < width) return (int)(square % size);
    int start = (length - width) / 2, middle = 0;
    for (int i = 0; i < width; ++i) middle = middle * 10 + digits[start + i] - '0';
    return middle % size;
}
/* Nonnegative key (0..1,000,000), table size (1..1000). Index only, no table insertion. */
int main(void) {
    int key, size;
    if (!readInt(&key) || !readInt(&size) || key < 0 || size < 1 || size > 1000) return invalid();
    printf("Hash index: %d\n",hash(key,size)); return 0;
}

Local compile: gcc -std=c17 mid-square-hash.c -o program -lm

Open in C compiler transfers the source. Copy the sample input into the compiler’s input box separately before running.

UNDERSTAND THE CODE

Line-by-line explanation

Follow the exact source above, from input handling through the algorithm and its result. Line numbers count blank lines; related statements are grouped into steps. Start with main to follow execution, then open the functions it calls.

Start with main → · Algorithm and data structure →

Lines 1–5Include input, conversion and validation declarations
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

stdio.h supplies terminal input/output. stdlib.h declares conversion and allocation functions. string.h supplies string and memory utilities. ctype.h supplies isspace, and errno.h supplies conversion error reporting. Headers declare facilities; including them does not execute an operation.

Lines 7–9Define a complete-token reader and initialise its state
/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
    int ch; size_t used = 0; int fits = 1;

readToken receives a writable buffer and its capacity. ch is int so it can represent EOF as well as input characters. used counts stored characters, and fits starts as a success flag. One slot must remain for the null terminator.

Lines 10–11Skip leading whitespace and recognise end-of-file
    while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
    if (ch == EOF) return 0;

Read until a non-whitespace character appears. The unsigned-char cast supplies an allowed value to isspace. Return 0 if EOF arrives before a token begins.

Lines 12–15Collect a token without silently truncating it
    do {
        if (ch == 0) fits = 0;
        if (used + 1 < capacity) text[used++] = (char)ch;
        else fits = 0;

The do body handles the character already read. An embedded zero byte marks the token invalid. Store a character only while there is room for it and a null terminator; otherwise clear fits. Continue reading even after the buffer fills.

Lines 16–20Consume the full token and terminate the buffer
        ch = getchar();
    } while (ch != EOF && !isspace((unsigned char)ch));
    text[used] = '\0';
    return fits;
}

Read through the next whitespace or EOF, add a null character after the stored prefix, then return fits. A too-long token returns 0 rather than pretending that its stored prefix is the complete token.

Lines 21–23Wrap token input as a checked integer read
int readInt(int *value) {
    char token[64], *end;
    if (!readToken(token, sizeof token)) return 0;

value is an output address. A 64-slot local buffer receives the token; a failed token read returns 0 before attempting conversion.

Lines 24–25Convert base-ten text and check the whole result
    errno = 0; long result = strtol(token, &end, 10);
    if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;

Reset errno before strtol. &end receives the stopping address. Reject conversion errors, no converted digits, leftover characters, or values outside -1,000,000 to 1,000,000. Thus 12x is not accepted as the integer 12.

Lines 26–28Return the accepted value or a standard error status
    *value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }

Store a validated integer through *value and return 1 from readInt. invalid is a separate helper that prints the error and returns 1 as main’s failure status; the same number has different roles in these two function contracts.

Lines 30–32Square the key and convert the square into decimal digits
int hash(int key, int size) {
    long long square = (long long)key * key;
    char digits[32]; snprintf(digits,sizeof digits,"%lld",square);

Cast before multiplication so the square uses long long. snprintf writes its decimal representation into the bounded digits buffer, including a null terminator.

Lines 33–35Choose how many middle digits to keep
    int width = 1; for (int maxIndex = size - 1; maxIndex >= 10; maxIndex /= 10) ++width;
    int length = (int)strlen(digits);
    if (length < width) return (int)(square % size);

width is the decimal digit count needed for the maximum index size - 1, with a minimum of one. If the square has fewer digits, fall back to square % size rather than reading beyond its string.

Lines 36–37Extract a centred decimal substring as an integer
    int start = (length - width) / 2, middle = 0;
    for (int i = 0; i < width; ++i) middle = middle * 10 + digits[start + i] - '0';

start uses (length - width) / 2, which picks the left-centred choice when the difference is odd. Build middle one digit at a time; subtracting '0' converts a decimal digit character into its value.

Lines 38–39Reduce the middle value to a legal index
    return middle % size;
}

Return middle % size. This program uses this specific decimal-width and centring convention; other mid-square examples may choose a different fixed width.

Lines 40–41Document an index-only hash demonstration
/* Nonnegative key (0..1,000,000), table size (1..1000). Index only, no table insertion. */
int main(void) {

The input is a nonnegative key followed by a table size. The program calculates an index only; it does not store keys or resolve collisions.

Lines 42–43Read a valid key and positive table size
    int key, size;
    if (!readInt(&key) || !readInt(&size) || key < 0 || size < 1 || size > 1000) return invalid();

readInt already limits integer magnitude. Require key >= 0 and size from 1 to 1000, preventing zero-divisor calculations and negative table indexes.

Lines 44–45Print the calculated index and finish
    printf("Hash index: %d\n",hash(key,size)); return 0;
}

Call hash(key,size), display the returned index and return success. A valid hash index need not be unique: different keys can still collide.

FOLLOW THE VALUES

Dry run

  1. 1234 squared is 1522756, containing seven digits.
  2. For m = 100, the extraction width is two digits.
  3. The start is floor((7−2)/2) = 2, so the selected digits are 22.
  4. The final index is 22 % 100 = 22.

CHECK THE BEHAVIOUR

Sample input & output

These outputs were captured by running the exact C source shown above. Commands use the stated input format; the programs omit repeated menu prompts so the same sample can be pasted and run.

Sample 1

INPUT
1234 100
OUTPUT
Hash index: 22

Sample 2

INPUT
10 17
OUTPUT
Hash index: 10

Sample 3

INPUT
1000000 1000
OUTPUT
Hash index: 0

Common mistakes

  • Widen before squaring; a widened destination cannot repair int multiplication overflow.
  • Specify the exact digit window.
  • Reduce the window modulo size for non-power-of-ten table sizes.

WHY THIS GROWTH RATE?

Time and space complexity

O(D + W) digit work; O(D) representation storage, with fixed bounds making this example O(1).

Formatting the square touches D decimal digits. Reading the selected window touches W digits; finding the width also takes O(log m).

The accepted key and table-size bounds cap D and W, so the standalone demonstration performs bounded work and reserves a fixed character buffer.

The definition is a decimal teaching convention. Different mid-square window conventions can yield different indexes, so examples must use the published rule.

Big-O describes growth in the stated input quantity. The educational limits keep examples safe; complexity discussions state which bounds depend on a generalized input size. Arithmetic on the accepted native numeric types is treated as constant-time.

PREPARE FOR YOUR LAB VIVA

Related viva questions & answers

6 focused questions

Try answering aloud, then expand the answer to check your reasoning. These questions focus on the concepts used in this program.

Why does the manual’s (square / 10) % 100 not always select the middle digits?

It always selects the same two decimal places counted from the right. The actual center changes with the square’s length, so a general central-window rule needs that length.

What is the extraction width for a table of size 100?

The largest index is 99, which needs two decimal digits. This version therefore chooses a two-digit central window.

Which window is chosen when left and right margins cannot be equal?

Start is floor((length - width)/2). This chooses the earlier of the two near-centered windows and makes the convention deterministic.

Why is a final modulo applied to the extracted digits?

A width-based decimal window can exceed m - 1 when m is not a power of ten. Modulo maps it back into the valid table interval.

What happens when the square has fewer digits than the required window?

The program uses the entire square and reduces it modulo m. It does not read nonexistent leading digits from the character buffer.

Why is (long long)key applied before multiplication?

It makes both the calculation and its result wide enough for the accepted squared keys. Casting only after an overflowing int product would be too late.