/**
  * Attempts to construct a map based on given DBC, predicting what each field could possibly hold by way of sampling
  */
 public static function fromDBC(DBC $dbc, $attach = true)
 {
     $fields = $dbc->getFieldCount();
     $samples = $dbc->getRecordCount() > self::SAMPLES ? self::SAMPLES : $dbc->getRecordCount();
     $block = $dbc->getStringBlock();
     preg_match_all('#\\0#', $block, $matches, PREG_OFFSET_CAPTURE);
     $strings = array();
     foreach ($matches[0] as $offset) {
         $offset = (int) $offset[1] + 1;
         if ($offset < strlen($block) - 1) {
             $strings[$offset] = true;
         }
     }
     $matrix = array_fill(1, $fields, 0);
     for ($i = 0; $i < $samples; $i++) {
         $record = $dbc->getRecord($i);
         $values = $record->asArray();
         foreach ($values as $offset => $value) {
             if ($value < 0) {
                 $matrix[$offset] += 1 << 0;
             }
             if (self::isProbableFloat($value)) {
                 $matrix[$offset] += 1 << 8;
             }
             if (isset($strings[$value]) || $value === 0) {
                 $matrix[$offset] += 1 << 16;
                 if ($value !== 0) {
                     $matrix[$offset] |= 1 << 24;
                 }
             }
         }
     }
     $map = new self();
     for ($i = 1; $i <= $fields; $i++) {
         $probs = $matrix[$i];
         $int = ($probs & 0xff) / $samples;
         $flt = (($probs & 0xff00) >> 8) / $samples;
         $str = (($probs & 0xff0000) >> 16) / $samples;
         $strbit = ($probs & 0xff000000) >> 24;
         $field = 'field' . $i;
         if ($flt > 0.6) {
             $type = DBC::FLOAT;
         } else {
             if ($strbit > 0 && $str > 0.99) {
                 $type = DBC::STRING;
                 if ($i + DBC::LOCALIZATION <= $fields) {
                     $type = DBC::STRING_LOC;
                     for ($j = $i + 1; $j <= $i + DBC::LOCALIZATION; $j++) {
                         $probs = $matrix[$j];
                         $str = (($probs & 0xff0000) >> 16) / $samples;
                         $strbit = ($probs & 0xff000000) >> 24;
                         if ($str !== 1 || $strbit !== 0) {
                             $type = DBC::STRING;
                         }
                     }
                     if ($type === DBC::STRING_LOC) {
                         $i += DBC::LOCALIZATION;
                     }
                 }
             } else {
                 if ($int > 0.01) {
                     $type = DBC::INT;
                 } else {
                     $type = DBC::UINT;
                 }
             }
         }
         $map->add($field, $type);
     }
     if ($attach && $dbc->getMap() === null) {
         $dbc->attach($map);
     }
     return $map;
 }
 /**
  * Exports given DBC in SQL format to given target (defaults to output stream) using given table name
  */
 public function export(DBC $dbc, $target = self::OUTPUT, $table = 'dbc')
 {
     $target = $target === null ? self::OUTPUT : $target;
     $map = $dbc->getMap();
     if ($map === null) {
         throw new DBCException(self::NO_MAP);
         return;
     }
     $sql = fopen($target, 'w+');
     $table = "`" . $this->escape($table) . "`";
     fwrite($sql, "DROP TABLE IF EXISTS " . $table . ";" . PHP_EOL . PHP_EOL);
     $dd = array();
     $fields = $map->getFields();
     foreach ($fields as $name => $rule) {
         $count = max($rule & 0xff, 1);
         $null = false;
         if ($rule & DBCMap::UINT_MASK) {
             $type = 'INT(11) UNSIGNED';
         } else {
             if ($rule & DBCMap::INT_MASK) {
                 $type = 'INT(11) SIGNED';
             } else {
                 if ($rule & DBCMap::FLOAT_MASK) {
                     $type = 'FLOAT';
                 } else {
                     if ($rule & DBCMap::STRING_MASK || $rule & DBCMap::STRING_LOC_MASK) {
                         $type = 'TEXT';
                         $null = true;
                     }
                 }
             }
         }
         for ($i = 1; $i <= $count; $i++) {
             $suffix = $count > 1 ? $i : '';
             $dd[] = '	`' . $this->escape($name) . $suffix . '` ' . $type . ' ' . ($null ? 'NULL' : 'NOT NULL');
         }
     }
     reset($fields);
     fwrite($sql, "CREATE TABLE " . $table . " (" . PHP_EOL . implode(',' . PHP_EOL, $dd) . ',' . PHP_EOL . '	PRIMARY KEY (`' . key($fields) . '`)' . PHP_EOL . ');' . PHP_EOL . PHP_EOL);
     foreach ($dbc as $i => $record) {
         if ($i % $this->recordsPerQuery === 0) {
             fwrite($sql, "INSERT INTO " . $table . " VALUES" . PHP_EOL . "\t(");
         } else {
             fwrite($sql, "," . PHP_EOL . "\t(");
         }
         fwrite($sql, $this->join($record->extract()));
         if (($i + 1) % $this->recordsPerQuery === 0 || $i === $dbc->getRecordCount() - 1) {
             fwrite($sql, ");" . PHP_EOL);
         } else {
             fwrite($sql, ")");
         }
     }
     fclose($sql);
     return $target;
 }