#!/usr/bin/php
<?php
/*
txt2bf.phpcli - a simple ASCII to brainfuck converter in PHP
Copyright (C) 2006 JRRZZ.net

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*
Version: 0.5 - 2006/03/08 23:12
Tested with the bff interpreter (http://swapped.cc/bf/) and PHP 4.4.2
*/

//does it have to be binary safe (keep NULL characters)?
//warning: it will result in substantially larger brainfucks
$binarysafe = false;

function printchar($char, $int) { //print out char for int long
  $i = 0;
  while ($i < $int) {
    $return .= $char;
    $i++;
  }
  return $return;
}

function searchmem ($x, $s, $cptr) { //search in x for the closest value for s closest to the current ptr and return new ptr
  $i = 0;
  while ($i < count($x)) {
    $dis = $i - $cptr; //get the distance to the ptr
    if ($dis < 0) $dis *= -1; //invert if negative
    $div = $x[$i]-$s;
    if ($div < 0) $div *= -1; //invert if negative
    $div += $dis; //compensate for ptr shifting
    if ($i == 0) {
      $lowestdiv = $div;
      $ptr = 0;
    } else if ($div < $lowestdiv) {
      $ptr = $i;
      $lowestdiv = $div;
    }
    $i++;
  }
  return $ptr;
}

function ascii2bf($index, $decrementer) { //convert char array index to string brainfuck using int decrementer
  $out = printchar('+', $decrementer); //initial loop decrementer
  $i = 0;
  $muls = array();
  while ($i < count($index)) { //get best possible multipliers in one array
    $div = $index[$i]/$decrementer;
    $mod = fmod($index[$i], $decrementer);
    if ($mod <= 5) {
      $mul = floor($div);
    } else {
      $mul = floor($div)+1;
    }
    if (!in_array($mul, $muls)) $muls[] = $mul;
    $i++;
  }
  $i = 0;
  $x = array(); //this is our memory page
  $ptr = 0; //this is our pointer
  $x[$ptr] = 0; //after the loop it's decremented to 0
  $out .= '['; //create prefill loop
  while ($i < count($muls)) {
    $out .= '>'.printchar('+', $muls[$i]);
    $x[$i+1] = $muls[$i]*$decrementer; //put the values in memory
    $i++;
  }
  $out .= printchar('<', count($muls)).'-]'; //end of prefill loop
  $i = 0;
  while ($i < count($index)) {
    $loc = searchmem($x, $index[$i], $ptr);//get the closest value in memory
    if ($loc < $ptr) { //where do we go to?
      $dir = '<';
    } else {
      $dir = '>';
    }
    $dirdiv = $ptr-$loc; //how long? if 0 we stay
    if ($dirdiv < 0) $dirdiv *= -1; //invert if negative
    $out .= printchar($dir, $dirdiv);
    $ptr = $loc; //we have moved
    $offset = $index[$i] - $x[$ptr]; //get the offset, of 0 we don't do anything
    if ($offset > 0) { //and the direction
      $offdir = '+';
    } else {
      $offdir = '-';
    }
    $x[$ptr] = $index[$i]; //sync memory
    if ($offset < 0) $offset *= -1; //invert if negative
    $out .= printchar($offdir, $offset).'.'; //print out our char
    $i++;
  }
  return $out;
}

if ($argc != 2) {
  echo "Usage: ".$argv[0]." textfile\n";
  exit;
}
$file = @fopen($argv[1], 'r');
if (!$file) {
  echo "Could not read file: ".$argv[1]."\n";
  exit;
}
$txt = "";
while(!feof($file)) $txt .= fread($file, 1);
fclose($file);
$txt = preg_split('//', $txt); //get char array
$i = 0;
$smallest = 256; //max ASCII value + 1
$index = array();
while ($i < count($txt)) {
  if ($binarysafe) {
    $ascii = ord($txt[$i]);
    $index[] = $ascii; //put the ASCII values in an array
    if ($ascii < $smallest && $ascii > 0) $smallest = $ascii; //get the smallest ASCII value
  } else {
    if (ord($txt[$i]) != 0) { //filter out NULL characters (making it binary un-safe)
      $ascii = ord($txt[$i]);
      $index[] = $ascii; //put the ASCII values in an array
      if ($ascii < $smallest && $ascii > 0) $smallest = $ascii; //get the smallest ASCII value that is not zero
    }
  }
  $i++;
}
$dec = $smallest;
while ($dec > 0) {
  $bf = ascii2bf($index, $dec);
  if (($dec == $smallest) || (strlen($bf) < strlen($bff))) $bff = $bf; //use all the decrementers and get the shortest result (usually highest decrementer though)
  $dec--;
}
echo $bff."\n";
?>
