Shop Problem

Here is the PHP Solution to an actual interview question:

Create a class called Shop. It should contain two methods:

buy($now, $quantity, $expiry)

and 

sell($now, $quantity)

Method buy accepts 3 parameters:

  • now – an integer that represents the present time
  • quantity – an integer for the amount currently being brought into the shop
  • expiry – an integer that represents the time at which this shipment expires

Method sell accepts 2 parameters:

  • now – an integer that represents the present time
  • quantity – an integer for the amount currently being sold from the store

This shop does not sell expired goods and tries to sell from the stock that is going to expire first. The task is to implement this class.

<?php

class Shop
{

  public $queue;
  public $apples;
  public $quantity;

  public function __construct()
  {
    $this->queue  = new SplMinHeap();
    $this->apples = array();
  }

  public function check_apples($now)
  {
    while ($this->queue->top() <= $now)
    {
      $this->delete_current_apple();
    }
  }
  
  private function debug($msg){
    return;
    echo "<div style=\"border:1px solid black;\"><pre>";
    var_dump($msg);
    echo "</pre></div>";
  }

  public function buy($now, $quantity, $expiry)
  {
    $this->queue->insert(intval($expiry));
    $this->apples[intval($expiry)] = intval($quantity);
    $this->quantity                += intval($quantity);
    $this->check_apples($now);
    return $this->quantity;
  }

  private function get_current_apple_quantity()
  {
    $cur = intval($this->queue->top());
    return intval($this->apples[$cur]);
  }

  private function delete_current_apple()
  {
    $cur            = intval($this->queue->top());
    $this->queue->extract();
    $this->quantity -= $this->apples[$cur];
    unset($this->apples[$cur]);
  }

  private function remove_apples($req_quantity)
  {
    $quantity = intval($req_quantity);
    $this->debug("Removing $quantity from " . $this->get_current_apple_quantity() . "<br />");
    $this->debug($this->quantity);
    if ($this->get_current_apple_quantity() > $quantity)
    {
      $cur                = $this->queue->top();
      $this->apples[$cur] -= $quantity;
      $this->quantity     -= $quantity;
    }
    $this->debug($this->quantity);
  }

  private function sell_helper($req_quantity)
  {
    $quantity     = intval($req_quantity);
    $cur_quantity = 0;
    do
    {
      $this->debug("Starting with " . $this->quantity);
      $cur_apple_count = $this->get_current_apple_quantity();
      $cur_quantity    += $cur_apple_count;
      $quantity_this_round = ($quantity >= $cur_quantity)?$cur_quantity:($cur_quantity - $quantity);

      if ($quantity_this_round >= $cur_apple_count)
      {
        $this->debug("deleting $quantity_this_round >= $cur_apple_count");
        $this->delete_current_apple();
      }
      else
      {
        if ($quantity_this_round < $cur_apple_count)
        {
          $this->remove_apples($cur_apple_count - $quantity_this_round);
        }
        else{
          $this->debug("unreachable code");
        }
        $cur_quantity = $quantity;
      }
      $this->debug($this->quantity);
      $this->debug("while ($cur_quantity < $quantity);<br />");
    }
    while ($cur_quantity < $quantity);
  }

  public function sell($now, $quantity)
  {
    $this->check_apples($now);
    $this->sell_helper($quantity);
    return $this->quantity;
  }

}

 

Script to Incrementally Delete old Backups

This Script Assumes a folder with daily backups exists. It incrementally deletes backups. It is configurable from command line switches and comes with a default schedule already setup.

<?php

$backup_dir          = "/home/demo/backup";
$keep_files_days     = 14;
$extension_to_delete = "tgz";
$size_to_delete      = 0;
$test                = false;
$progressive         = true;
$log                 = true;
$date                = date("YmdHis");
$log_file            = "delete_old_backups_$date.log";
$log_path            = "/var/log/backup_scripts/";


$log_string = "---------------------------------------------------------\n"
  . "Starting Delete_old_backups script\nDate $date\n\n\n";

/**
 * @var Integer number of days to keep daily backups
 */
$daily   = 7;
$weekly  = 4;
$monthly = 12;

//check for command line argument
foreach ($argv as $arg)
{
  $item = explode("=", $arg);
  switch ($item[0])
  {
    case "dir":
      $backup_dir          = $item[1];
      break;
    case "keep":
      $keep_files_days     = $item[1];
      break;
    case "extension":
      $extension_to_delete = $item[1];
      break;
    case "size":
      $size_to_delete      = $item[1];
      break;
    case "progressive":
      $progressive         = $item[1];
      break;
    case "daily":
      $daily               = $item[1];
      break;
    case "weekly":
      $weekly              = $item[1];
      break;
    case "monthly":
      $monthly             = $item[1];
      break;
    case "log":
      $log                 = $item[1];
      break;
    case "log_file":
      $log_file            = $item[1];
      break;
    case "log_path":
      $log_path            = $item[1];
      break;
    case "help":
      echo get_help();
      break;
    case "test":
      $test                = ($item[1] === "true");
      break;
  }
}

/**
 * Setting up timestamps to compare daily, weekly, monthly
 */
$daily_days          = $daily;
$weekly_days         = $weekly * 7;
$monthly_days        = $monthly * 30;
$daily_file_string   = date("YmdHis", strtotime("-$daily_days day")) . ".tgz";
$weekly_file_string  = date("YmdHis", strtotime("-$weekly_days day")) . ".tgz";
$monthly_file_string = date("YmdHis", strtotime("-$monthly_days day")) . ".tgz";
$first_month         = true;
$first_week          = true;

$weekly_position  = $weekly - 1;
$monthly_position = $monthly - 1;

$weekly_array = array();
for ($i = ($weekly - 1); $i >= 0; $i--)
{
  $week_start_date  = ($i * 7);
  $week_end_date    = ($i * 7) - 7;
  $weekly_array[$i] = array(
    "keep"           => false,
    "start_filename" => date("YmdHis", strtotime("-$week_start_date day")) . ".tgz",
    "end_filename"   => date("YmdHis", strtotime("-$week_end_date day")) . ".tgz",
  );
}

$monthly_array = array();
for ($i = ($monthly - 1); $i >= 0; $i--)
{
  $month_start_date  = ($i * 30);
  $month_end_date    = ($i * 30) - 30;
  $monthly_array[$i] = array(
    "keep"           => false,
    "start_filename" => date("YmdHis", strtotime("-$month_start_date day")) . ".tgz",
    "end_filename"   => date("YmdHis", strtotime("-$month_end_date day")) . ".tgz",
  );
}

/**
 * Listing files in directory
 */
$timestamp_to_keep = strtotime("-$keep_files_days day");
$files             = scandir($backup_dir);
$date_to_keep      = date("Ymd", $timestamp_to_keep);

foreach ($files as $file)
{
  $log_string .= "found file $file\n";
  $to_delete  = false;
  $file_path  = $backup_dir . "/" . $file;
  if (endsWith($file, ".$extension_to_delete"))
  {

    if ($progressive)
    {
      $daily_cmp = strcmp($file, $daily_file_string);
      /**
       * Backups are taken daily so we do nothing for the files less than the daily threshold
       */
      if ($daily_cmp < 0)
      {
        $weekly_cmp = strcmp($file, $weekly_file_string);

        /**
         * Checking for weekly files
         */
        if ($weekly_cmp >= 0)
        {
          if (can_delete($weekly_array, $weekly_position, $file, $first_week))
          {
            $to_delete = true;
          }
        }
        else
        {
          if (can_delete($monthly_array, $monthly_position, $file, $first_month))
          {
            $to_delete = true;
          }
        }//end monthly (else greater than 4 weeks ago)
      }
    }//end if $progressive = true
    else
    {
      //  echo "ends with $extension_to_delete\n";
      $timestamp_cmp = strcmp($file, $date_to_keep);
      //echo $timestamp_cmp . "\n";
      if (($timestamp_cmp < 0) && ($progressive == false ))
      {
        $to_delete = true;
        if ($debug){
          echo "to_delete $file\n";
        }
        //echo $file_path . "\n";
      }//end check for time and progressive == false;
    }//end else  (progressive == false)

    if ($to_delete)
    {
      $log_string .= "Deleting $file\n";
      if (!$test)
      {
        unlink($file_path);
      }
    }//end if($to_delete)
    else
    {
      $log_string .= "Keeping $file\n";
    }//end else (!$to_delete)
  }//end check for timestamp
}//end  foreach file
//var_dump($test);
$ret = file_put_contents($log_path . $log_file, $log_string, FILE_APPEND);
//var_dump($ret);
exit();

/* * ********************************************************************************** */

function can_delete(&$periods, &$period_pointer, $current_file, &$first_period)
{
  if (in_period($periods, $period_pointer, $current_file, $first_period))
  {
    if ($periods[$period_pointer]["keep"] == false)
    {
      $periods[$period_pointer]["keep"] = $current_file;
      return false;
    }
    else
    {
      return true;
    }
  }
  return true;
}

function in_period(&$periods, &$period_pointer, $current_file, &$first_period)
{
  $start_cmp = strcmp($periods["$period_pointer"]["start_filename"], $current_file);
  $end_cmp   = strcmp($periods["$period_pointer"]["end_filename"], $current_file);
  while (($end_cmp < 0) && ($period_pointer >= 0))
  {// && ($first_period === true)) {
    $period_pointer--;
    $start_cmp = strcmp($periods["$period_pointer"]["start_filename"], $current_file);
    $end_cmp   = strcmp($periods["$period_pointer"]["end_filename"], $current_file);
  }
  $first_period = false;


  if (($start_cmp < 0) && ($end_cmp >= 0))
  {
    return true;
  }
  return false;
}

function endsWith($haystack, $needle)
{
  $length = strlen($needle);
  if ($length == 0)
  {
    return true;
  }
  return (substr($haystack, -$length) === $needle);
}

function get_help()
{
  return <<<help
  Script to delete old backups.

  Options:

  help -- show this message

    dir={dir} -- Change the location of the backups

    case "progressive":

    case "daily":

    case "weekly":

    case "monthly":

    case "log":

    case "log_file":
  
    case "log_path":
help;
}