1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace chippyash\Type\Number\Rational;
12:
13: use chippyash\Type\Exceptions\InvalidTypeException;
14: use chippyash\Type\Number\IntType;
15: use chippyash\Type\Number\GMPIntType;
16: use chippyash\Type\Number\FloatType;
17: use chippyash\Type\AbstractTypeFactory;
18:
19: 20: 21:
22: abstract class RationalTypeFactory extends AbstractTypeFactory
23: {
24: 25: 26:
27: const CF_DEFAULT_TOLERANCE = 1.e-15;
28:
29: 30: 31: 32: 33: 34: 35: 36:
37: protected static $defaultTolerance = self::CF_DEFAULT_TOLERANCE;
38:
39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:
53: public static function create($numerator, $denominator = null)
54: {
55: if (is_string($numerator)) {
56: return self::fromString($numerator);
57: }
58:
59: if (is_float($numerator) || $numerator instanceof FloatType) {
60: return self::fromFloat($numerator);
61: }
62:
63: if (is_numeric($numerator)) {
64: return self::createFromNumericNumerator($numerator, $denominator);
65: }
66:
67: if ($numerator instanceof IntType) {
68: return self::createFromIntTypeNumerator($numerator, $denominator);
69: }
70:
71: self::throwCreateException($numerator, $denominator);
72: }
73:
74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85:
86: public static function fromFloat($float, $tolerance = null)
87: {
88: if ($float instanceof FloatType) {
89: $float = $float();
90: }
91: if ($float == 0.0) {
92: return new RationalType(new IntType(0), new IntType(1));
93: }
94: if ($tolerance instanceof FloatType) {
95: $tolerance = $tolerance();
96: } elseif (is_null($tolerance)) {
97: $tolerance = self::$defaultTolerance;
98: }
99:
100: $negative = ($float < 0);
101: if ($negative) {
102: $float = abs($float);
103: }
104: $num1 = 1;
105: $num2 = 0;
106: $den1 = 0;
107: $den2 = 1;
108: $oneOver = 1 / $float;
109: do {
110: $oneOver = 1 / $oneOver;
111: $floor = floor($oneOver);
112: $aux = $num1;
113: $num1 = $floor * $num1 + $num2;
114: $num2 = $aux;
115: $aux = $den1;
116: $den1 = $floor * $den1 + $den2;
117: $den2 = $aux;
118: $oneOver = $oneOver - $floor;
119: } while (abs($float - $num1 / $den1) > $float * $tolerance);
120:
121: if ($negative) {
122: $num1 *= -1;
123: }
124:
125: return self::createCorrectRational($num1, $den1);
126: }
127:
128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139:
140: public static function fromString($string)
141: {
142: $matches = array();
143: $valid = \preg_match(
144: '#^(-)? *?(\d+) *?/ *?(-)? *?(\d+)$#',
145: \trim($string),
146: $matches
147: );
148:
149: if ($valid !== 1) {
150: throw new \InvalidArgumentException(
151: 'The string representation of the rational is invalid.'
152: );
153: }
154:
155: $numerator = $matches[2];
156: $denominator = $matches[4];
157:
158: if ($matches[1] xor $matches[3]) {
159:
160: $numerator *= -1;
161: }
162:
163: return self::createCorrectRational($numerator, $denominator);
164: }
165:
166: 167: 168: 169: 170: 171: 172: 173:
174: public static function setDefaultFromFloatTolerance($tolerance)
175: {
176: self::$defaultTolerance = $tolerance;
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186:
187: protected static function createCorrectRational($num, $den)
188: {
189: if (self::getRequiredType() == self::TYPE_GMP) {
190: return new GMPRationalType(new GMPIntType($num), new GMPIntType($den));
191: } else {
192: return new RationalType(new IntType($num), new IntType($den));
193: }
194: }
195:
196: 197: 198: 199: 200: 201: 202: 203: 204: 205:
206: private static function createFromNumericNumerator($numerator, $denominator)
207: {
208: if (is_null($denominator)) {
209: return self::createCorrectRational($numerator, 1);
210: }
211:
212: if (is_numeric($denominator)) {
213: return self::createCorrectRational($numerator, $denominator);
214: }
215:
216: if ($denominator instanceof IntType) {
217: return self::createCorrectRational($numerator, $denominator());
218: }
219:
220: self::throwCreateException($numerator, $denominator);
221: }
222:
223: 224: 225: 226: 227: 228: 229: 230: 231: 232:
233: private static function createFromIntTypeNumerator(IntType $numerator, $denominator)
234: {
235: if ($denominator instanceof IntType) {
236: return self::createCorrectRational($numerator(), $denominator());
237: }
238:
239: if (is_null($denominator)) {
240: return self::createCorrectRational($numerator(), 1);
241: }
242:
243: if (is_numeric($denominator)) {
244: return self::createCorrectRational($numerator(), $denominator);
245: }
246:
247: self::throwCreateException($numerator, $denominator);
248: }
249:
250: 251: 252: 253: 254: 255: 256: 257:
258: private static function throwCreateException($numerator, $denominator)
259: {
260: $typeN = gettype($numerator);
261: $typeD = gettype($denominator);
262: throw new InvalidTypeException("{$typeN}:{$typeD} for Rational type construction");
263: }
264: }
265: