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\RationalTypeFactory;
15: use chippyash\Type\Exceptions\NotRealComplexException;
16:
17: /**
18: * A complex number - algabraic form
19: *
20: * A complex number is a number that can be expressed in the form a + bi,
21: * where a and b are real numbers and i is the imaginary unit,
22: * which satisfies the equation i² = −1
23: *
24: * Complex numbers use real numbers expressed as a RationalType. This allows
25: * for greater arithmetic stability
26: *
27: * @link http://en.wikipedia.org/wiki/Complex_number
28: */
29: class ComplexType extends AbstractComplexType
30: {
31: /**
32: * Map of values for this type
33: * @var array
34: */
35: protected $valueMap = array(
36: 0 => array(
37: 'name' => 'real',
38: 'class' => 'chippyash\Type\Number\Rational\RationalType'
39: ),
40: 1 => array(
41: 'name' => 'imaginary',
42: 'class' => 'chippyash\Type\Number\Rational\RationalType'
43: )
44: );
45:
46: /**
47: * Constructor
48: *
49: * @param RationalType $real
50: * @param RationalType $imaginary
51: */
52: public function __construct(RationalType $real, RationalType $imaginary)
53: {
54: $this->setFromTypes(array($real, $imaginary));
55: }
56:
57: /**
58: * Return the number as a Complex number i.e. n+0i
59: *
60: * @return \chippyash\Type\Number\Complex\ComplexType
61: */
62: public function asComplex()
63: {
64: return clone $this;
65: }
66:
67: /**
68: * Return number as Rational number.
69: * NB, numerator and denominator will be caste as IntTypes
70: *
71: * @return \chippyash\Type\Number\Rational\RationalType
72: *
73: * @throws NotRealComplexException
74: */
75: public function asRational()
76: {
77: if ($this->isReal()) {
78: return clone $this->value['real'];
79: } else {
80: throw new NotRealComplexException();
81: }
82: }
83:
84: /**
85: * Return the modulus, also known as absolute value or magnitude of this number
86: * = sqrt(r^2 + i^2);
87: *
88: * @return \chippyash\Type\Number\Rational\RationalType
89: */
90: public function modulus()
91: {
92: if ($this->isReal()) {
93: //sqrt(r^2 + 0^2) = sqrt(r^2) = abs(r)
94: /** @noinspection PhpUndefinedMethodInspection */
95: return $this->value['real']->abs();
96: }
97: //r^2 & i^2
98: $sqrR = array(
99: 'n'=>pow($this->value['real']->numerator()->get(), 2),
100: 'd'=>pow($this->value['real']->denominator()->get(), 2)
101: );
102: $sqrI = array(
103: 'n'=>pow($this->value['imaginary']->numerator()->get(), 2),
104: 'd'=>pow($this->value['imaginary']->denominator()->get(), 2)
105: );
106: //r^2 + i^2
107: $den = $this->lcm($sqrR['d'], $sqrI['d']);
108: $num = ($sqrR['n'] * $den / $sqrR['d']) +
109: ($sqrI['n'] * $den / $sqrI['d']);
110:
111: //sqrt(num/den) = sqrt(num)/sqrt(den)
112: //now this a fudge - we ought to be able to get a proper square root using
113: //factors etc but what we do instead is to do an approximation by converting
114: //to intermediate rationals i.e.
115: // rNum = RationaType(sqrt(num))
116: // rDen = RationalType(sqrt(den))
117: // mod = rN/1 * 1/rD
118: $rNum = RationalTypeFactory::fromFloat(sqrt($num));
119: $rDen = RationalTypeFactory::fromFloat(sqrt($den));
120: $modN = $rNum->numerator()->get() * $rDen->denominator()->get();
121: $modD = $rNum->denominator()->get() * $rDen->numerator()->get();
122:
123: return RationalTypeFactory::create($modN, $modD);
124: }
125:
126: /**
127: * Return the angle (sometimes known as the argument) of the number
128: * when expressed in polar notation
129: *
130: * The return value is a rational expressing theta as radians
131: *
132: * @return \chippyash\Type\Number\Rational\RationalType
133: */
134: public function theta()
135: {
136: return RationalTypeFactory::fromFloat(
137: atan2(
138: $this->value['imaginary']->asFloatType()->get(),
139: $this->value['real']->asFloatType()->get()
140: )
141: );
142: }
143:
144: /**
145: * Return Greatest Common Denominator of two numbers
146: *
147: * @param int $a
148: * @param int $b
149: *
150: * @return int
151: */
152: private function gcd($a, $b)
153: {
154: return $b ? $this->gcd($b, $a % $b) : $a;
155: }
156:
157: /**
158: * Return Least Common Multiple of two numbers
159: * @param int $a
160: * @param int $b
161: * @return int
162: */
163: private function lcm($a, $b)
164: {
165: return \abs(($a * $b) / $this->gcd($a, $b));
166: }
167: }
168: