Untitled

mail@pastecode.io avatar
unknown
c_cpp
8 months ago
7.0 kB
2
Indexable
Never
#ifndef _WINDOW_BIT_COUNT_APX_
#define _WINDOW_BIT_COUNT_APX_
#define max(a, b) (((a) > (b)) ? (a) : (b))
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t N_MERGES = 0; // keep track of how many bucket merges occur

/*
    TODO: You can add code here.
*/
typedef struct Bucket {
  uint32_t count; // Count of 1's in the bucket
  uint32_t ts;    // ts or index of the most recent bit in the bucket
} Bucket;

typedef struct {
  // TODO: Fill me.
  Bucket *buckets;      // Array of buckets
  uint32_t maxBuckets;  // Maximum number of buckets
  uint32_t bucketCount; // Current number of buckets
  uint32_t wndSize;     // Size of the window
  uint32_t *bitBuffer;  // Circular buffer for bits
  uint32_t bufferIndex; // Current index in the circular bit buffer
  uint32_t k;           // 1/eps, controls precision
  uint32_t totalBitsProcessed;
} StateApx;

// k = 1/eps
// if eps = 0.01 (relative error 1%) then k = 100
// if eps = 0.001 (relative error 0.1%) the k = 1000
uint64_t wnd_bit_count_apx_new(StateApx *self, uint32_t wnd_size, uint32_t k) {
  assert(wnd_size >= 1);
  assert(k >= 1);

  // TODO: Fill me.
  self->maxBuckets = 2 * (uint32_t)log2(wnd_size);
  // TODO:
  // The function should return the total number of bytes allocated on the heap.
  size_t bucketsSize = sizeof(Bucket) * self->maxBuckets;
  size_t bufferSize = sizeof(uint32_t) * wnd_size;
  uint8_t *allocatedMemory = (uint8_t *)malloc(bucketsSize + bufferSize);
  if (!allocatedMemory) // Handle allocation failure
    return 0;
  self->buckets = (Bucket *)(allocatedMemory);
  self->bitBuffer = (uint32_t *)(allocatedMemory + bucketsSize);
  self->bucketCount = 0;
  self->wndSize = wnd_size;
  self->bufferIndex = 0;
  self->totalBitsProcessed = 0;
  self->k = k;

  // Return total bytes allocated
  return bucketsSize + bufferSize;
}

void wnd_bit_count_apx_destruct(StateApx *self) {
  // TODO: Fill me.
  // Make sure you free the memory allocated on the heap.
  free(self);
  return;
}

void wnd_bit_count_apx_print(StateApx *self) {
  printf("Total bits: %u\n", self->totalBitsProcessed);
  printf("Window Size: %u\n", self->wndSize);
  printf("Bucket Count: %u\n", self->bucketCount);
  printf("Buckets:\n");

  // Print details of each bucket
  for (uint32_t i = 0; i < self->bucketCount; ++i) {
    printf("  Bucket %u: Count = %u, Timestamp = %u\n", i,
           self->buckets[i].count, self->buckets[i].ts);
  }

  // Optionally, print the bit buffer if it's not too large
  // Uncomment the following lines if you want to include the bit buffer
  // contents in the debug output
  /*
  printf("Bit Buffer:\n  ");
  for (uint32_t i = 0; i < self->wndSize; ++i) {
      printf("%u", self->bitBuffer[i]);
      if ((i + 1) % 50 == 0) // Break line every 50 bits for readability
          printf("\n  ");
  }
  printf("\n");
  */

  printf("\n"); // Add an extra newline for readability
}

void discardOldBuckets(StateApx *self) {
  // This loop will check all buckets and discard those that are too old
  while (self->bucketCount > 0) {
    // Calculate the age of the oldest bucket
    uint64_t ageOfOldestBucket = self->totalBitsProcessed - self->buckets[0].ts;

    // If the oldest bucket is outside of our window of interest, we discard it
    if (ageOfOldestBucket >= self->wndSize) {
      // Shift all buckets to the left to discard the oldest one
      for (uint32_t i = 1; i < self->bucketCount; ++i) {
        self->buckets[i - 1] = self->buckets[i];
      }
      self->bucketCount--; // Decrease the bucket count
    } else {
      // If the oldest bucket is still within the window, we stop discarding
      break;
    }
  }
}

uint32_t wnd_bit_count_apx_next(StateApx *self, bool item) {
  // Update the circular buffer with the incoming bit
  self->bitBuffer[self->bufferIndex] = item;
  uint32_t ts = self->totalBitsProcessed;
  self->bufferIndex = (self->bufferIndex + 1) % self->wndSize;
  self->totalBitsProcessed++;
  // printf("buffer Index = %d\n", self->bufferIndex);
  if (item) {
    // Add a new bucket for the "1" bit if space allows
    if (self->bucketCount < self->maxBuckets) {
      self->buckets[self->bucketCount++] = (Bucket){1, ts};
    }

    // Merge buckets as necessary
    uint32_t size = 1;
    while (size <= self->wndSize) {
      int bucketIndex = -1;
      int count = 0;
      // Find the oldest buckets of the current size
      for (uint32_t i = 0; i < self->bucketCount; ++i) {
        if (self->buckets[i].count == size) {
          if (bucketIndex == -1 ||
              self->buckets[i].ts < self->buckets[bucketIndex].ts) {
            bucketIndex = i;
          }
          count++;
        }
      }

      // If there are more than two buckets of the same size, merge the oldest
      // two
      if (count > 2 && bucketIndex != -1) {
        int secondOldestIndex = -1;
        // Find the second oldest bucket of the current size
        for (uint32_t i = 0; i < self->bucketCount; ++i) {
          if (self->buckets[i].count == size && i != bucketIndex) {
            if (secondOldestIndex == -1 ||
                self->buckets[i].ts < self->buckets[secondOldestIndex].ts) {
              secondOldestIndex = i;
            }
          }
        }

        // Merge the two oldest buckets
        if (secondOldestIndex != -1) {
          self->buckets[bucketIndex].count *= 2;
          self->buckets[bucketIndex].ts =
              max(self->buckets[bucketIndex].ts,
                  self->buckets[secondOldestIndex].ts);
          // Move the last bucket to the position of the second oldest and
          // reduce the bucket count
          self->buckets[secondOldestIndex] = self->buckets[--self->bucketCount];
        }
      } else {
        break;
      }
      size *= 2; // Move to the next size of buckets
    }
  }

  discardOldBuckets(self);

  // Estimate the count of "1"s in the last N bits
  uint32_t count = 0;
  // Iterate through each bucket to compute the count of "1"s in the last N bits
  for (uint32_t i = 0; i < self->bucketCount; ++i) {
    // Calculate how far back in the stream this bucket's bits go
    uint64_t startOfWindow;
    if (self->totalBitsProcessed >= self->wndSize) {
      startOfWindow = self->totalBitsProcessed - self->wndSize + 1;
    } else {
      startOfWindow = 0;
    }
    uint64_t startOfBucket = self->buckets[i].ts - self->buckets[i].count + 1;
    // printf("start of window %u\n", startOfWindow);
    // printf("start of bucket %u\n", startOfBucket);
    //  Check if the bucket is completely within theprintf("start of window
    //  %d\n", startOfWindow); last N bits
    count += self->buckets[i].count;
    if (startOfBucket < startOfWindow)
      count -= self->buckets[i].count / self->k;
  }

  return count;
}

#endif // _WINDOW_BIT_COUNT_APX_
Leave a Comment