Skip to content

Commit

Permalink
Merge pull request #2 from danielfdsilva/feature/expressions
Browse files Browse the repository at this point in the history
Allow the use of expressions to initialize and compute intervals.
  • Loading branch information
Daniel Silva committed Oct 11, 2014
2 parents f0688ff + 288eb07 commit fd8fdcf
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 12 deletions.
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ To create a MathInterval instance simply use:
```php
$interval = new MathInterval('[5,20]');
```
With this you just created an interval that compreends all numbers from 5 to 20 (including 5 and 20).
With this you just created an interval that comprehends all numbers from 5 to 20 (including 5 and 20).
Now you can start validating number against this interval:
```php
$interval->inInterval(5); // TRUE
Expand All @@ -32,7 +32,7 @@ $interval->inInterval(150); // FALSE
$interval->inInterval(15.25); // FALSE
```
What?! How come 15.25 is ```FALSE```. Isn't it between 5 and 20?
To simplify validation, MathInterval assumes that only integer values are valid but don't dispair. If you need to allow float numbers you just need to initialize the interval with one of the values as float:
To simplify validation, MathInterval assumes that only integer values are valid but don't despair. If you need to allow float numbers you just need to initialize the interval with one of the values as float:
```php
// Initializing one of the values as float, makes the interval handle them.
$interval = new MathInterval('[5.0,20]');
Expand Down Expand Up @@ -65,6 +65,29 @@ $interval->intersection('[17,33[');
print $interval; // [17,20]
```

### Expressions
MathInterval also allows you to use expressions to initialize and compute intervals. The official symbols for intersections and union are ∩ and ⋃. However, since these symbols are hard to type MathInterval uses ```and``` and ```or```.

For example:
```php
// [5,20] ⋃ [10,25]
$interval = new MathInterval('[5,20] or [10,25]');
print $interval; // [5,25]

// [5,20] ∩ [10,25]
$interval = new MathInterval('[5,20] and [10,25]');
print $interval; // [20,25]

// You can chain as many values as needed in an expression:
// [5,20] ∩ [10,25] ⋃ [15,30[
$interval = new MathInterval('[5,20] and [10,25] or [15,30[');
print $interval; // [10,30[

// You can also use these expressions in the union() and intersection() methods.
```
> Note that like in mathematical expressions the order of operators matters and as in mathematics you can use parenthesis.
For example: ```[5,20] ∩ ([10,25] ⋃ [15,30[)``` = ```[5,20] ∩ [10,30[``` = ```[10,20]```

-----

## Contribution
Expand Down
9 changes: 9 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Changelog

### 1.1.0

- Add support for expressions.

### 1.0.0

- Initial release.
25 changes: 15 additions & 10 deletions src/MathInterval.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* @author Daniel da Silva
* @package MathInterval
* @version 1.0.0
* @version 1.1.0
*/

// In class constants you can't define constants with concatenation
Expand All @@ -15,7 +15,7 @@
// Match: [1,2] or [3,4] and [5,6].
define('MATH_INTERVAL_EXPRESSION_REGEX', MATH_INTERVAL_REGEX . '(?: (?:or|and) '.MATH_INTERVAL_REGEX.')*');
// Match: ([1,2] or [3,4]).
define('MATH_INTERVAL_EXP_ATOM_REGEX', '\(('.MATH_INTERVAL_EXPRESSION_REGEX.'\)');
define('MATH_INTERVAL_EXP_ATOM_REGEX', '\(('.MATH_INTERVAL_EXPRESSION_REGEX.')\)');

/**
* Library to use Mathematical Intervals.
Expand All @@ -30,7 +30,7 @@ class MathInterval {
/**
* MathInterval version.
*/
const VERSION = '1.0.0';
const VERSION = '1.1.0';

/**
* Whether the beginning of the interval is closed.
Expand Down Expand Up @@ -204,7 +204,7 @@ static function compute($expression) {
return (is_int($lbound) && is_int($ubound)) ? ']0,0[' : ']0.0,0.0[';
}
}
/*elseif (preg_match('/^'.MATH_INTERVAL_EXPRESSION_REGEX.'$/', $expression, $results)) {
elseif (preg_match('/^'.MATH_INTERVAL_EXPRESSION_REGEX.'$/', $expression, $results)) {
// There are no atoms to extract.
// Proceed to compute.
$atom = $results[0];
Expand All @@ -228,13 +228,18 @@ static function compute($expression) {
while ($op = next($ranges));

return $working_range->__toString();
}*/
/*elseif (preg_match('/'.MATH_INTERVAL_EXP_ATOM_REGEX.'/', $expression, $atoms)) {
}
elseif (preg_match('/'.MATH_INTERVAL_EXP_ATOM_REGEX.'/', $expression, $atoms)) {
// Extracted atom.
$atom = $atoms[1];
print "$atom\n";
compute($atom);
}*/
// $atoms[0] contains the expression with parenthesis.
// $atoms[1] contains the expression without parenthesis.
$simplified = MathInterval::compute($atoms[1]);
// Replace the atom with the simplified expression.
// If multiple matches are found all will be replaced.
$expression = str_replace($atoms[0], $simplified, $expression);
// Run the compute again with the new expression.
return MathInterval::compute($expression);
}
else {
throw new MathIntervalException("Invalid expression.");
}
Expand Down
41 changes: 41 additions & 0 deletions tests/MathInterval/MathIntervalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ public function dataProviderExceptionInvalid() {
array('(1,2)'),
array('1,2'),
array('[1,2] or 3'),

// The function contructor relies on compute().
// That function can be tested through this.
array('[1,2] orq [3,4]'),
array('[1,2] adn [3,4]'),
array('[1,2] or [3,5] and, [3,10]'),

// Test expressions with parenthesis.
array('[1,2] or [3,5] and ()[3,10])'),
array('[1,2] or [3,5] and (((([3,10])))'),
array('(([1,2] or (([3,5]) and [3,10]))'),
array('([1,2] or ([3,5] and) [3,10])'),
);
}

Expand Down Expand Up @@ -61,6 +73,35 @@ public function dataProviderCompute() {
array('[1.0,1[', ']0.0,0.0['),
array(']1.0,1]', ']0.0,0.0['),
array(']1.0,1[', ']0.0,0.0['),


// The other tests will test the union and intersection methods
// exhaustively. Here we only test if the expected results are
// correct since the used methods are common.
array('[1,5] or [3,9]', '[1,9]'),
array('[3,9] or [1,5]', '[1,9]'),
array('[3,9] or [1,5] or [10,13]', '[1,13]'),
array('[3,9] or [1,5] or [10,13] or ]1,2[', '[1,13]'),
array('[3,9] or [1,5] or [10,13] or ]0,0[', '[1,13]'),

array('[1,5] and [3,9]', '[3,5]'),
array('[1,50] and [3,45] and ]40,41]', ']40,41]'),
array('[1,50] and [3,45] and ]0,0[', ']0,0['),

// With union and intersection order is very important.
array('[1,5] and [3,9] or [10,15]', '[3,15]'),
array('[3,9] or [10,15] and [1,5]', '[3,5]'),
array('[3,9] or [9,15] and [15,20]', '[15,15]'),
array('[3,9] or [9,15] and ]15,20]', ']0,0['),

// Test expressions with parenthesis.
array('([1,5])', '[1,5]'),
array('(([1,5]))', '[1,5]'),
array('[1,5] and ([3,9] or [10,15])', '[3,5]'),
array('[3,9] or ([10,15] and ([1,5]))', '[3,9]'),
array('([3,9] or ([9,15] and ([15,20])))', '[3,15]'),
array('(([3,9[) and [9,15]) and ]15,20]', ']0,0['),
array('([1,5] and [3,4]) and ([1,10] or ([1,5] and [3,4]))', '[3,4]'),
);
}

Expand Down

0 comments on commit fd8fdcf

Please sign in to comment.