#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];
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);

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;
}
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);
}
}

int is_item_in_cache(node *n, int item) {
for (int i = 0; i < CACHE_SIZE; i++) {
if (n->cache[i] == item) {
return 1;
}
}
return 0;
}

void handle_csim_error_message(long err) {
if (err != 0) {
printf(“Error: %s\n”, csim_error_message[err]);
exit(1);
}
}


this code does not implement the LRU cache replacement policy, you will need to implement the cache eviction/update logic based on the LRU policy.