1: <?php
2: /**
3: * Hard type support
4: * For when you absolutely want to know what you are getting
5: *
6: * Thanks to Florian Wolters for the inspiration
7: *
8: * @copyright Ashley Kitson, UK, 2012
9: * @author Ashley Kitson <akitson@zf4.biz>
10: * @licence GPL V3 or later : http://www.gnu.org/licenses/gpl.html
11: *
12: * @link http://github.com/FlorianWolters/PHP-Component-Number-Fraction
13: */
14: namespace chippyash\Type\Number\Rational;
15:
16: use chippyash\Type\AbstractMultiValueType;
17: use chippyash\Type\Number\IntType;
18: use chippyash\Type\Interfaces\RationalTypeInterface;
19: use chippyash\Type\Interfaces\NumericTypeInterface;
20: use chippyash\Type\Number\Complex\ComplexType;
21: use chippyash\Type\Number\FloatType;
22:
23: /**
24: * Abstract rational number type
25: */
26: abstract class AbstractRationalType extends AbstractMultiValueType implements RationalTypeInterface, NumericTypeInterface
27: {
28: /**
29: * Do we reduce to lowest form on construct and set?
30: *
31: * @var boolean
32: */
33: protected $reduce = true;
34:
35: /**
36: * Map of values for this type
37: * @var array
38: */
39: protected $valueMap = array(
40: 0 => array('name' => 'num', 'class' => 'chippyash\Type\Interfaces\NumericTypeInterface'),
41: 1 => array('name' => 'den', 'class' => 'chippyash\Type\Interfaces\NumericTypeInterface')
42: );
43:
44: /**
45: * Return the number as a Complex number i.e. n+0i
46: *
47: * @return \chippyash\Type\Number\Complex\ComplexType
48: */
49: public function asComplex()
50: {
51: return new ComplexType(
52: new RationalType(clone $this->numerator(), clone $this->denominator()),
53: new RationalType(new IntType(0), new IntType(1))
54: );
55: }
56:
57: /**
58: * Return number as Rational number.
59: * NB, numerator and denominator will be caste as IntTypes
60: *
61: * @return \chippyash\Type\Number\Rational\RationalType
62: */
63: public function asRational()
64: {
65: return clone $this;
66: }
67:
68: /**
69: * Return number as an IntType number.
70: * Will return floor(n/d)
71: *
72: * @return \chippyash\Type\Number\IntType
73: */
74: public function asIntType()
75: {
76: return new IntType(floor($this->get()));
77: }
78:
79: /**
80: * Return number as a FloatType number.
81: *
82: * @return \chippyash\Type\Number\FloatType
83: */
84: public function asFloatType()
85: {
86: return new FloatType($this->get());
87: }
88:
89: /**
90: * Get the numerator
91: * @return mixed
92: */
93: public function numerator()
94: {
95: return $this->value['num'];
96: }
97:
98: /**
99: * Get the denominator
100: *
101: * @return mixed
102: */
103: public function denominator()
104: {
105: return $this->value['den'];
106: }
107:
108: /**
109: * Return the absolute value of the number
110: *
111: * @return \chippyash\Type\Number\Rational\RationalType
112: */
113: public function abs()
114: {
115: /** @noinspection PhpUndefinedMethodInspection */
116: return new static($this->value['num']->abs(), $this->value['den']->abs());
117: }
118:
119: /**
120: * Negates the number
121: *
122: * @return \chippyash\Type\Number\Rational\RationalType Fluent Interface
123: */
124: public function negate()
125: {
126: $this->value['num']->negate();
127:
128: return $this;
129: }
130:
131:
132: /**
133: * Magic method - convert to string
134: * Returns "<num>/<den>" or "<num>" if isInteger()
135: *
136: * @return string
137: */
138: public function __toString()
139: {
140: $num = $this->value['num']->get();
141: if ($this->isInteger()) {
142: return "{$num}";
143: } else {
144: $den = $this->value['den']->get();
145: return "{$num}/{$den}";
146: }
147: }
148:
149: /**
150: * Get the basic PHP value of the object type properly
151: * In this case, the type is an int or float
152: *
153: * @return int|float
154: */
155: public function getAsNativeType()
156: {
157: if ($this->isInteger()) {
158: return intval($this->value['num']->get());
159: } else {
160: return floatval($this->value['num']->get() / $this->value['den']->get());
161: }
162: }
163:
164: /**
165: * Is this Rational an expression of an integer, i.e. n/1
166: *
167: * @return boolean
168: */
169: public function isInteger()
170: {
171: return ($this->value['den']->get() === 1);
172: }
173:
174: /**
175: * Reduce this number to it's lowest form
176: *
177: * @return void
178: */
179: abstract protected function reduce();
180:
181: /**
182: * Maps values passed in as parameters to constructor and set methods into
183: * the value array
184: *
185: * @param array $params
186: *
187: * @return void
188: *
189: * @throws \InvalidArgumentException
190: */
191: protected function setFromTypes(array $params)
192: {
193: parent::setFromTypes($params);
194:
195: if ($this->reduce) {
196: $this->reduce();
197: }
198:
199: if ($this->value['den']->get() < 0) {
200: //normalise the sign
201: $this->value['num']->negate();
202: $this->value['den']->negate();
203: }
204: }
205: }
206: