/**
  * Filters the records in the record set via a callback
  * 
  * The `$callback` parameter can be one of three different forms to filter
  * the records in the set:
  * 
  *  - A callback that accepts a single record and returns `FALSE` if it should be removed
  *  - A psuedo-callback in the form `'{record}::methodName'` to filter out any records where the output of `$record->methodName()` is equivalent to `FALSE`
  *  - A conditions array that will remove any records that don't meet all of the conditions
  * 
  * The conditions array can use one or more of the following `key => value`
  * syntaxes to perform various comparisons. The array keys are method
  * names followed by a comparison operator.
  * 
  * {{{
  * // The following forms work for any $value that is not an array
  * 'methodName='                           => $value  // If the output is equal to $value
  * 'methodName!'                           => $value  // If the output is not equal to $value
  * 'methodName!='                          => $value  // If the output is not equal to $value
  * 'methodName<>'                          => $value  // If the output is not equal to $value
  * 'methodName<'                           => $value  // If the output is less than $value
  * 'methodName<='                          => $value  // If the output is less than or equal to $value
  * 'methodName>'                           => $value  // If the output is greater than $value
  * 'methodName>='                          => $value  // If the output is greater than or equal to $value
  * 'methodName~'                           => $value  // If the output contains the $value (case insensitive)
  * 'methodName!~'                          => $value  // If the output does not contain the $value (case insensitive)
  * 'methodName|methodName2|methodName3~'   => $value  // Parses $value as a search string and make sure each term is present in at least one output (case insensitive)
  * 
  * // The following forms work for any $array that is an array
  * 'methodName='                           => $array  // If the output is equal to at least one value in $array
  * 'methodName!'                           => $array  // If the output is not equal to any value in $array
  * 'methodName!='                          => $array  // If the output is not equal to any value in $array
  * 'methodName<>'                          => $array  // If the output is not equal to any value in $array
  * 'methodName~'                           => $array  // If the output contains one of the strings in $array (case insensitive)
  * 'methodName!~'                          => $array  // If the output contains none of the strings in $array (case insensitive)
  * 'methodName&~'                          => $array  // If the output contains all of the strings in $array (case insensitive)
  * 'methodName|methodName2|methodName3~'   => $array  // If each value in the array is present in the output of at least one method (case insensitive)
  * 
  * // The following works for an equal number of methods and values in the array
  * 'methodName!|methodName2<|methodName3=' => array($value, $value2, $value3) // An OR statement - one of the method to value comparisons must be TRUE
  * 
  * // The following accepts exactly two methods and two values, although the second value may be NULL
  * 'methodName|methodName2><'              => array($value, $value2) // If the range of values from the methods intersects the range of $value and $value2 - should be dates, times, timestamps or numbers
  * }}} 
  * 
  * @param  callback|string|array $procedure                The way in which to filter the records - see method description for possible forms
  * @param  boolean               $remember_original_count  If the number of records in the current set should be saved as the non-limited count for the new set - the page will be reset to `1` either way
  * @return fRecordSet  A new fRecordSet with the filtered records
  */
 public function filter($procedure, $remember_original_count = FALSE)
 {
     if (!$this->records) {
         return clone $this;
     }
     if (is_array($procedure) && is_string(key($procedure))) {
         $type = 'conditions';
         $conditions = $procedure;
     } elseif (is_string($procedure) && preg_match('#^\\{record\\}::([a-z0-9_\\-]+)$#iD', $procedure, $matches)) {
         $type = 'psuedo-callback';
         $method = $matches[1];
     } else {
         $type = 'callback';
         $callback = $procedure;
         if (is_string($callback) && strpos($callback, '::') !== FALSE) {
             $callback = explode('::', $callback);
         }
     }
     $new_records = array();
     $classes = !is_array($this->class) ? array($this->class => TRUE) : array();
     foreach ($this->records as $record) {
         switch ($type) {
             case 'conditions':
                 $value = fActiveRecord::checkConditions($record, $conditions);
                 break;
             case 'psuedo-callback':
                 $value = $record->{$method}();
                 break;
             case 'callback':
                 $value = call_user_func($callback, $record);
                 break;
         }
         if ($value) {
             $classes[get_class($record)] = TRUE;
             $new_records[] = $record;
         }
     }
     return new fRecordSet(array_keys($classes), $new_records, $remember_original_count ? $this->count() : NULL);
 }