1: <?php
2: /**
3: * Hard type support
4: * For when you absolutely want to know what you are getting
5: *
6: * @author Ashley Kitson <akitson@zf4.biz>
7: * @copyright Ashley Kitson, UK, 2014
8: * @licence GPL V3 or later : http://www.gnu.org/licenses/gpl.html
9: */
10:
11: namespace chippyash\Type\Number\Complex;
12:
13: use chippyash\Type\Number\Rational\RationalType;
14: use chippyash\Type\Number\Rational\GMPRationalType;
15: use chippyash\Type\Number\Rational\RationalTypeFactory;
16: use chippyash\Type\Exceptions\NotRealComplexException;
17: use chippyash\Type\Interfaces\GMPInterface;
18: use chippyash\Type\Number\GMPIntType;
19: use chippyash\Type\Number\FloatType;
20: use chippyash\Type\TypeFactory;
21:
22: /**
23: * A complex number - algabraic form - GMP version
24: *
25: * A complex number is a number that can be expressed in the form a + bi,
26: * where a and b are real numbers and i is the imaginary unit,
27: * which satisfies the equation i² = −1
28: *
29: * Complex numbers use real numbers expressed as a GMPRationalType. This allows
30: * for greater arithmetic stability
31: *
32: * @link http://en.wikipedia.org/wiki/Complex_number
33: */
34: class GMPComplexType extends AbstractComplexType implements GMPInterface
35: {
36:
37: /**
38: * Map of values for this type
39: * @var array
40: */
41: protected $valueMap = array(
42: 0 => array(
43: 'name' => 'real',
44: 'class' => 'chippyash\Type\Number\Rational\GMPRationalType'
45: ),
46: 1 => array(
47: 'name' => 'imaginary',
48: 'class' => 'chippyash\Type\Number\Rational\GMPRationalType'
49: )
50: );
51:
52: /**
53: * Constructor
54: *
55: * @param GMPRationalType $real
56: * @param GMPRationalType $imaginary
57: */
58: public function __construct(GMPRationalType $real, GMPRationalType $imaginary)
59: {
60: $this->setFromTypes(array($real, $imaginary));
61: }
62:
63: /**
64: * Return the modulus, also known as absolute value or magnitude of this number
65: * = sqrt(r^2 + i^2);
66: *
67: * @return \chippyash\Type\Number\Rational\GMPRationalType
68: */
69: public function modulus()
70: {
71: if ($this->isReal()) {
72: //sqrt(r^2 + 0^2) = sqrt(r^2) = abs(r)
73: /** @noinspection PhpUndefinedMethodInspection */
74: return $this->value['real']->abs();
75: }
76: //get r^2 and i^2
77: $sqrR = array(
78: 'n' => gmp_pow($this->value['real']->numerator()->gmp(), 2),
79: 'd' => gmp_pow($this->value['real']->denominator()->gmp(), 2)
80: );
81: $sqrI = array(
82: 'n' => gmp_pow($this->value['imaginary']->numerator()->gmp(), 2),
83: 'd' => gmp_pow($this->value['imaginary']->denominator()->gmp(), 2)
84: );
85: //r^2 + i^2
86: $den = $this->lcm($sqrR['d'], $sqrI['d']);
87: $numRaw = gmp_strval(
88: gmp_add(
89: gmp_div_q(gmp_mul($sqrR['n'], $den), $sqrR['d']),
90: gmp_div_q(gmp_mul($sqrI['n'], $den), $sqrI['d'])
91: )
92: );
93: $num = TypeFactory::createInt($numRaw);
94:
95: //sqrt(num/den) = sqrt(num)/sqrt(den)
96: //now this a fudge - we ought to be able to get a proper square root using
97: //factors etc but what we do instead is to do an approximation by converting
98: //to intermediate rationals using as much precision as we can i.e.
99: // rNum = GMPRationaType(sqrt(num))
100: // rDen = GMPRationalType(sqrt(den))
101: // mod = rN/1 * 1/rD
102: $rNum = RationalTypeFactory::fromFloat(sqrt($num()));
103: $rDen = RationalTypeFactory::fromFloat(sqrt(gmp_strval($den)));
104: $modN = gmp_mul($rNum->numerator()->gmp(), $rDen->denominator()->gmp());
105: $modD = gmp_mul($rNum->denominator()->gmp(), $rDen->numerator()->gmp());
106:
107: return RationalTypeFactory::create(
108: (int) gmp_strval($modN),
109: (int) gmp_strval($modD)
110: );
111: }
112:
113: /**
114: * Return the angle (sometimes known as the argument) of the number
115: * when expressed in polar notation
116: *
117: * The return value is a rational expressing theta as radians
118: *
119: * @todo implement gmp atan2 method
120: *
121: * @return \chippyash\Type\Number\Rational\GMPRationalType
122: */
123: public function theta()
124: {
125: return RationalTypeFactory::fromFloat(
126: atan2(
127: $this->value['imaginary']->asFloatType()->get(),
128: $this->value['real']->asFloatType()->get()
129: )
130: );
131: }
132:
133: /**
134: * Return the value of number array of gmp resources|objects
135: *
136: * @return \GMP|\resource array [[num,den],[num,den]]
137: */
138: public function gmp()
139: {
140: return array($this->value['real']->gmp(), $this->value['imaginary']->gmp());
141: }
142:
143: /**
144: * Return number as GMPIntType number.
145: * If number isReal() will return floor(r())
146: *
147: * @return \chippyash\Type\Number\GMPIntType
148: * @throws NotRealComplexException
149: */
150: public function asGMPIntType()
151: {
152: if ($this->isReal()) {
153: return new GMPIntType(floor($this->value['real']->get()));
154: } else {
155: throw new NotRealComplexException();
156: }
157: }
158:
159: /**
160: * Return the number as a GMPComplex number i.e. a+bi
161: * Clones self
162: *
163: * @return \chippyash\Type\Number\Complex\GMPComplexType
164: */
165: public function asGMPComplex()
166: {
167: return clone $this;
168: }
169:
170: /**
171: * Return the number as a Complex number i.e. n+0i
172: *
173: * @return \chippyash\Type\Number\Complex\ComplexType
174: */
175: public function asComplex()
176: {
177: return new ComplexType($this->r()->asRational(), $this->i()->asRational());
178: }
179:
180: /**
181: * Return number as GMPRational number.
182: * If number isReal() will return GMPRationalType
183: * NB, numerator and denominator will be caste as GMPIntTypes
184: *
185: * @return \chippyash\Type\Number\Rational\GMPRationalType
186: * @throws NotRealComplexException
187: */
188: public function asGMPRational()
189: {
190: if ($this->isReal()) {
191: return clone $this->value['real'];
192: } else {
193: throw new NotRealComplexException();
194: }
195: }
196:
197: /**
198: * Return number as Rational number.
199: * NB, numerator and denominator will be caste as IntTypes
200: *
201: * @return \chippyash\Type\Number\Rational\RationalType
202: *
203: * @throws NotRealComplexException
204: */
205: public function asRational()
206: {
207: if ($this->isReal()) {
208: return new RationalType(
209: $this->value['real']->numerator()->asIntType(),
210: $this->value['real']->denominator()->asIntType()
211: );
212: } else {
213: throw new NotRealComplexException();
214: }
215: }
216:
217: /**
218: * Return number as a FloatType number.
219: *
220: * @return \chippyash\Type\Number\FloatType
221: * @throws NotRealComplexException
222: */
223: public function asFloatType()
224: {
225: if ($this->isReal()) {
226: return new FloatType($this->value['real']->get());
227: } else {
228: throw new NotRealComplexException();
229: }
230: }
231:
232: /**
233: * Return Least Common Multiple of two numbers
234: * @param int $a
235: * @param int $b
236: * @return int
237: */
238: private function lcm($a, $b)
239: {
240: return gmp_abs(gmp_div_q(gmp_mul($a, $b), gmp_gcd($a, $b)));
241: }
242: }
243: