Overview

Namespaces

  • chippyash
    • Type
      • Exceptions
      • Interfaces
      • Number
        • Complex
        • Rational
      • String

Classes

  • AbstractComplexType
  • ComplexType
  • ComplexTypeFactory
  • GMPComplexType
  • Overview
  • Namespace
  • Class
  • Tree
  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\AbstractTypeFactory;
 14: use chippyash\Type\Exceptions\InvalidTypeException;
 15: use chippyash\Type\Number\Rational\RationalType;
 16: use chippyash\Type\Number\Rational\GMPRationalType;
 17: use chippyash\Type\Number\Rational\AbstractRationalType;
 18: use chippyash\Type\Number\Rational\RationalTypeFactory;
 19: use chippyash\Type\Number\FloatType;
 20: use chippyash\Type\Number\IntType;
 21: use chippyash\Type\TypeFactory;
 22: 
 23: /**
 24:  * Static Factory for creating complex types
 25:  *
 26:  * A complex number is a number that can be expressed in the form a + bi,
 27:  * where a and b are real numbers and i is the imaginary unit,
 28:  * which satisfies the equation i² = −1
 29:  */
 30: abstract class ComplexTypeFactory extends AbstractTypeFactory
 31: {
 32:     /**
 33:      * Complex type factory
 34:      *
 35:      * Construct and return a complex number. You can send in
 36:      * - a string conforming to 'a+bi', 'a-bi', '-a+bi', '-a-bi' where a & b
 37:      * are integer or float numbers e.g. '-12+0.67i'
 38:      * - mixture of numeric (int,float,'1234' etc), IntType and FloatType corresponding to a & b
 39:      *
 40:      * @param string|float|int|FloatType|IntType $realPart
 41:      * @param float|int|FloatType|IntType $imaginaryPart
 42:      *
 43:      * @return \chippyash\Type\Number\Complex\ComplexType
 44:      *
 45:      * @throws \InvalidArgumentException
 46:      */
 47:     public static function create($realPart, $imaginaryPart = null)
 48:     {
 49:         if (is_string($realPart)) {
 50:             return self::fromString($realPart);
 51:         }
 52: 
 53:         if (is_null($imaginaryPart)) {
 54:             throw new \InvalidArgumentException('Imaginary part may not be null if real part is not a string');
 55:         }
 56: 
 57:         $real = self::convertType($realPart);
 58:         $imaginary = self::convertType($imaginaryPart);
 59:         
 60:         if (self::getRequiredType() == self::TYPE_GMP) {
 61:             return new GMPComplexType($real, $imaginary);
 62:         } else {
 63:             return new ComplexType($real, $imaginary);
 64:         }
 65:     }
 66: 
 67:     /**
 68:      * Create a complex number from a string in form '[+,-]<num>(+,-)<num>i'
 69:      * The trailing 'i' character is required
 70:      * No spaces are allowed in the string
 71:      *
 72:      * @param string $string
 73:      *
 74:      * @return \chippyash\Type\Number\Complex\ComplexType
 75:      *
 76:      * @throws \InvalidArgumentException
 77:      */
 78:     public static function fromString($string)
 79:     {
 80:         $matches = array();
 81:         $valid = \preg_match(
 82:             '#^([-,\+])?([0-9]*[\.\/]?[0-9]*)([-,\+]){1}([0-9]*[\.\/]?[0-9]*)i$#',
 83:             \trim($string),
 84:             $matches
 85:         );
 86: 
 87:         if ($valid !== 1) {
 88:             throw new \InvalidArgumentException(
 89:                 'The string representation of the complex number is invalid.'
 90:             );
 91:         }
 92: 
 93:         $real = $matches[2];
 94:         $imaginary = $matches[4];
 95: 
 96:         if ($matches[1] && $matches[1] == '-') {
 97:             $real = '-' . $real;
 98:         }
 99:         if ($matches[3] && $matches[3] == '-') {
100:             $imaginary = '-' . $imaginary;
101:         }
102:         
103:         return self::create(self::convertType($real), self::convertType($imaginary));
104:     }
105: 
106:     /**
107:      * Create complex type from polar co-ordinates
108:      * 
109:      * Be aware that you may lose a bit of precision e.g.
110:      * $c = ComplexTypeFactory::create('2/7+3/4i');
111:      * $c2 = ComplexTypeFactory::fromPolar($c->radius(), $c->theta());
112:      * returns 132664833738225/464326918083788+3382204885901775/4509606514535696i
113:      * which is ~0.2857142857142854066 + ~0.75000000000000066525i
114:      * whereas the original is 
115:      * ~0.28571428571428571429 + 0.75i
116:      * 
117:      * formula for conversion is z = r(cos(theta) + i.sin(theta))
118:      * 
119:      * @param \chippyash\Type\Number\Rational\AbstractRationalType $radius
120:      * @param \chippyash\Type\Number\Rational\AbstractRationalType $theta angle expressed in radians
121:      * 
122:      * @return \chippyash\Type\Number\Complex\ComplexType
123:      */
124:     public static function fromPolar(AbstractRationalType $radius, AbstractRationalType $theta)
125:     {
126:         if (self::getRequiredType() == self::TYPE_GMP) {
127:             return self::fromGmpPolar($radius, $theta);
128:         } else {
129:             return self::fromNativePolar($radius, $theta);
130:         }
131:     }
132:     
133:     /**
134:      * Create complex type from polar co-ordinates - Native version
135:      * 
136:      * z = radius x (cos(theta) + i.sin(theta))
137:      *   real = radius x cos(theta)
138:      *   imag = radius x sin(theta)
139:      * 
140:      * @param \chippyash\Type\Number\Rational\RationalType $radius
141:      * @param \chippyash\Type\Number\Rational\RationalType $theta angle expressed in radians
142:      * 
143:      * @return \chippyash\Type\Number\Complex\ComplexType
144:      */
145:     public static function fromNativePolar(RationalType $radius, RationalType $theta)
146:     {
147:         $cos = RationalTypeFactory::fromFloat(cos($theta()));
148:         $sin = RationalTypeFactory::fromFloat(sin($theta()));
149: 
150:         list($realNumerator, $realDenominator) = self::getRealPartsFromRadiusAndCos($radius, $cos);
151:         list($imaginaryNumerator, $imaginaryDenominator) = self::getImaginaryPartsFromRadiusAndSin($radius, $sin);
152: 
153:         $realPart = RationalTypeFactory::create($realNumerator, $realDenominator);
154:         $imaginaryPart = RationalTypeFactory::create($imaginaryNumerator, $imaginaryDenominator);
155:         
156:         return new ComplexType($realPart, $imaginaryPart);
157:     }
158: 
159:     /**
160:      * Create complex type from polar co-ordinates - GMP version
161:      * 
162:      * @param \chippyash\Type\Number\Rational\GMPRationalType $radius
163:      * @param \chippyash\Type\Number\Rational\GMPRationalType $theta angle expressed in radians
164:      * 
165:      * @return \chippyash\Type\Number\Complex\GMPComplexType
166:      */
167:     public static function fromGmpPolar(GMPRationalType $radius, GMPRationalType $theta)
168:     {
169:         $cos = RationalTypeFactory::fromFloat(cos($theta()));
170:         $sin = RationalTypeFactory::fromFloat(sin($theta()));
171: 
172:         //real = radius * cos
173:         $rNum = TypeFactory::create(
174:             'int',
175:             gmp_strval(
176:                 gmp_mul(
177:                     $radius->numerator()->gmp(),
178:                     $cos->numerator()->gmp()
179:                 )
180:             )
181:         );
182:         $rDen = TypeFactory::create(
183:             'int',
184:             gmp_strval(
185:                 gmp_mul(
186:                     $radius->denominator()->gmp(),
187:                     $cos->denominator()->gmp()
188:                 )
189:             )
190:         );
191:         //imag = radius * sin
192:         $iNum = TypeFactory::create(
193:             'int',
194:             gmp_strval(
195:                 gmp_mul(
196:                     $radius->numerator()->gmp(),
197:                     $sin->numerator()->gmp()
198:                 )
199:             )
200:         );
201:         $iDen = TypeFactory::create(
202:             'int',
203:             gmp_strval(
204:                 gmp_mul(
205:                     $radius->denominator()->gmp(),
206:                     $sin->denominator()->gmp()
207:                 )
208:             )
209:         );
210: 
211:         return new GMPComplexType(
212:             RationalTypeFactory::create($rNum, $rDen),
213:             RationalTypeFactory::create($iNum, $iDen)
214:         );
215:     }
216: 
217:     /**
218:      * Convert to RationalType
219:      *
220:      * @param mixed $original
221:      *
222:      * @return \chippyash\Type\Number\Rational\RationalType|\chippyash\Type\Number\Rational\GMPRationalType
223:      *
224:      * @throws InvalidTypeException
225:      */
226:     protected static function convertType($original)
227:     {
228:         if ($original instanceof AbstractRationalType) {
229:             return RationalTypeFactory::create(
230:                 $original->numerator()->get(),
231:                 $original->denominator()->get()
232:             );
233:         }
234:         if (is_numeric($original)) {
235:             if (is_int($original)) {
236:                 return RationalTypeFactory::create($original, 1);
237:             }
238:             //default - convert to float
239:             return RationalTypeFactory::fromFloat(floatval($original));
240:         }
241:         if ($original instanceof FloatType) {
242:             return RationalTypeFactory::fromFloat($original());
243:         }
244:         if ($original instanceof IntType) {
245:             return RationalTypeFactory::create($original, 1);
246:         }
247:         if (is_string($original)) {
248:             try {
249:                 return RationalTypeFactory::fromString($original);
250:             } catch (\InvalidArgumentException $e) {
251:                 throw new InvalidTypeException("{$original} for Complex type construction");
252:             }
253:         }
254: 
255:         $type = gettype($original);
256:         throw new InvalidTypeException("{$type} for Complex type construction");
257:     }
258: 
259:     /**
260:      * @param RationalType $radius
261:      * @param RationalType $cos
262:      *
263:      * @return array
264:      *
265:      * @throws InvalidTypeException
266:      */
267:     private static function getRealPartsFromRadiusAndCos(RationalType $radius, RationalType $cos)
268:     {
269:         return array(
270:             TypeFactory::create('int', $radius->numerator()->get() * $cos->numerator()->get()),
271:             TypeFactory::create('int', $radius->denominator()->get() * $cos->denominator()->get())
272:         ) ;
273:     }
274: 
275:     /**
276:      * @param RationalType $radius
277:      * @param RationalType $sin
278:      *
279:      * @return array
280:      *
281:      * @throws InvalidTypeException
282:      */
283:     private static function getImaginaryPartsFromRadiusAndSin(RationalType $radius, RationalType $sin)
284:     {
285:         return array(
286:             TypeFactory::create('int', $radius->numerator()->get() * $sin->numerator()->get()),
287:             TypeFactory::create('int', $radius->denominator()->get() * $sin->denominator()->get())
288:         ) ;
289:     }
290: }
291: 
Chippyash Strong Types API documentation generated by ApiGen 2.8.0