#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "csim.h"

#define NUM_CLIENTS 5
#define DB_SIZE 500
#define DATA_ITEM_SIZE 1024
#define BANDWIDTH 10000
#define CACHE_SIZE 100
#define NUM_HOT_ITEMS 50
#define SIMULATION_TIME 10e4

double t_query_arr[] = {5, 10, 25, 50, 100};
double t_update_arr[] = {5, 10, 20, 100, 200};
float hot_access_prob = 0.8;
float hot_update_prob = 0.33;

typedef struct {
int id;
int cache[CACHE_SIZE];
int cache_age[CACHE_SIZE]; // Add this field to store age of each item
double q_gen_time;
double u_arr_time;
} node;


FACILITY server;
TABLE query_delay, cache_hit_ratio;
node *nodes;
float total_queries = 0;
float cache_hits = 0;

void client();
void generate_query(node *n);
void query_result(node *n);
int is_item_in_cache(node *n, int item);


void update_cache(node *n, int data_item) {
int min_age_index = 0;

for (int i = 0; i < CACHE_SIZE; i++) {
if (n->cache[i] == -1) { // Empty cache slot found
n->cache[i] = data_item;
n->cache_age[i] = 1;
return;
}

if (n->cache_age[i] < n->cache_age[min_age_index]) {
min_age_index = i;
}
}

// Replace the least recently used item in the cache
n->cache[min_age_index] = data_item;
n->cache_age[min_age_index] = 1;
}


int main() {
long err;
create("simple_pull_based_cache");
nodes = (node *)malloc(NUM_CLIENTS * sizeof(node));
err = facility("Server", &server);
handle_csim_error_message(err);
table_init("Query Delay", &query_delay);
table_init("Cache Hit Ratio", &cache_hit_ratio);

// Simulating
for (size_t q = 0; q < sizeof(t_query_arr) / sizeof(t_query_arr[0]); q++) {
for (size_t u = 0; u < sizeof(t_update_arr) / sizeof(t_update_arr[0]); u++) {
reset();

total_queries = 0;
cache_hits = 0;

for (int i = 0; i < NUM_CLIENTS; i++) {
nodes[i].id = i;
nodes[i].q_gen_time = t_query_arr[q];
nodes[i].u_arr_time = t_update_arr[u];
// Initialize cache

for (int j = 0; j < CACHE_SIZE; j++) {
nodes[i].cache[j] = -1;
nodes[i].cache_age[j] = -1; // Initialize age of each item to -1
}
node_activity(client);
}

schedule(START, 0.0);
schedule(END, SIMULATION_TIME);
simulate();

printf("Cache hit ratio: %.2f\n", cache_hits / total_queries);
printf("Query delay: %.2f\n", tbl_mean(query_delay));
printf("\n");
}
}

// Clean up
table_rm(query_delay);
table_rm(cache_hit_ratio);
err = facility_rm(server);
handle_csim_error_message(err);
cleanup();
free(nodes);

return 0;
}

void client() {
node *n = nodes[node_id()];
while (clock < SIMULATION_TIME) {
hold(exponential(n->q_gen_time));

float prob = lrand48() / (float)RAND_MAX;

// Hot data item or cold data item
int data_item;
if (prob < hot_access_prob) {
data_item = lrand48() % NUM_HOT_ITEMS + 1;
} else {
data_item = NUM_HOT_ITEMS + (lrand48() % (DB_SIZE - NUM_HOT_ITEMS));
}

generate_query(n);
}
}

void generate_query(node *n) {
request(server);
double q_start_time = clock;
hold(DATA_ITEM_SIZE * 8 / BANDWIDTH);
int cache_hit = is_item_in_cache(n, data_item);

if (cache_hit) {
cache_hits++;
release(server);

// Send a check message to the server
request(server);
hold(DATA_ITEM_SIZE * 8 / BANDWIDTH);

// Receive confirm message from the server
release(server);

} else {
// Request data item from the server
release(server);
query_result(n);
}

// Calculate query delay
double q_delay = clock - q_start_time;
sample(query_delay, q_delay);

total_queries++;
}


void query_result(node *n) {
hold(exponential(n->u_arr_time));

// Probability of hot data item update
float prob = lrand48() / (float)RAND_MAX;

if (prob < hot_update_prob) {
request(server);
hold(DATA_ITEM_SIZE * 8 / BANDWIDTH);

// Update hot data item
release(server);
}

// Update the cache based on the LRU policy
update_cache(n, data_item);
}
int is_item_in_cache(node *n, int item) {
int item_found = 0;
for (int i = 0; i < CACHE_SIZE; i++) {
if (n->cache[i] == item) {
n->cache_age[i] = 1;
item_found = 1;
} else {
n->cache_age[i]++;
}
}
return item_found;
}
void handle_csim_error_message(long err) {
if (err != 0) {
printf("Error: %s\n", csim_error_message[err]);
exit(1);
}
}

this code is for the following scenario 
Implement a simple pull-based cache invalidation scheme by using CSIM in C. In the scheme, a node generates a query and sends a request message to a server if the queried data item is not found in its local cache. If the node generates a query that can be answered by its local cache, it sends a check message to the server for validity. The server replies to the node with a requested data item or a confirm message. A set of data items is updated in the server. This scheme ensures a strong consistency that guarantees to access the most updated data items. Note that remove a cold state before collecting any results. use the following simulation parameters .make sure to output cache hit ratio and (ii) query delay by changing mean query generation time and mean update arrival time. 
Parameter Values 
Number of clients 5 
Database (DB) size 500 items 
Data item size 1024 bytes
 Bandwidth 10000 bits/s 
Cache size 100 items 
Cache replacement policy LRU 
Mean query generate time (T_query) 5, 10, 25, 50, 100 seconds 
Hot data items 1 to 50 
Cold data items remainder of DB 
Hot data access prob. 0.8 
Mean update arrival time (T_update) 5, 10, 20, 100, 200 seconds 
Hot data update prob. 0.33


generate a report containing Brief explanations of major functionalities, such as server(), client(), cache functions and all other functions, etc. and Any assumption and implementation issue.