ComparisonUtils::compareFloats()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 4
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
namespace Ivory\Value\Alg;
4
5
use Ivory\Exception\IncomparableException;
6
7
class ComparisonUtils
8
{
9
    public static function equals($a, $b): bool
10
    {
11
        if ($a === null) {
12
            return ($b === null);
13
        }
14
15
        if ($a instanceof IEqualable) {
16
            return $a->equals($b);
17
        } elseif (is_array($a)) {
18
            if (!is_array($b)) {
19
                return false;
20
            }
21
            if (count($a) != count($b)) {
22
                return false;
23
            }
24
            foreach ($a as $k => $v) {
25
                if (!array_key_exists($k, $b)) {
26
                    return false;
27
                }
28
                if (!self::equals($v, $b[$k])) {
29
                    return false;
30
                }
31
            }
32
            return true;
33
        } else {
34
            return ($a == $b);
35
        }
36
    }
37
38
    /**
39
     * Compares two values according to their type.
40
     *
41
     * @param mixed $a
42
     * @param mixed $b
43
     * @return int the comparison result: negative integer, zero, or positive integer if <tt>$this</tt> is less than,
44
     *               equal to, or greater than <tt>$other</tt>, respectively
45
     * @throws IncomparableException if the values are incomparable
46
     * @throws \InvalidArgumentException if either of values is <tt>null</tt>
47
     */
48
    public static function compareValues($a, $b): int
49
    {
50
        if ($a === null || $b === null) {
51
            throw new \InvalidArgumentException('comparing with null');
52
        }
53
54
        if (is_int($a) || is_bool($a)) {
55
            return ($a <=> $b);
56
        } elseif (is_float($a)) {
57
            return self::compareFloats($a, $b);
58
        } elseif (is_string($a)) {
59
            return strcmp($a, $b);
60
        } elseif ($a instanceof IComparable) {
61
            return $a->compareTo($b);
62
        } elseif (is_array($a) && is_array($b)) {
63
            return self::compareArrays($a, $b);
64
        } else {
65
            throw new IncomparableException();
66
        }
67
    }
68
69
    public static function compareBigIntegers($a, $b): int
70
    {
71
        if ($a > PHP_INT_MAX || $b > PHP_INT_MAX || $a < PHP_INT_MIN || $b < PHP_INT_MIN) {
72
            return bccomp($a, $b, 0);
73
        } else {
74
            return (int)$a - (int)$b;
75
        }
76
    }
77
78
    public static function compareFloats($a, $b): int
79
    {
80
        if ($a === NAN) {
81
            return ($b === NAN ? 0 : 1);
82
        } elseif ($b === NAN) {
83
            return -1;
84
        }
85
86
        return (float)$a <=> (float)$b;
87
    }
88
89
    public static function compareArrays(array $a, array $b): int
90
    {
91
        reset($b);
92
        foreach ($a as $av) {
93
            if (key($b) === null) {
94
                return 1;
95
            }
96
            $bv = current($b);
97
            next($b);
98
            if ($av === null) {
99
                if ($bv !== null) {
100
                    return -1;
101
                }
102
            } elseif ($bv === null) {
103
                return 1;
104
            } elseif (is_array($av)) {
105
                if (is_array($bv)) {
106
                    $comp = self::compareArrays($av, $bv);
107
                    if ($comp) {
108
                        return $comp;
109
                    }
110
                } else {
111
                    return 1;
112
                }
113
            } elseif (is_array($bv)) {
114
                return -1;
115
            } else {
116
                $comp = self::compareValues($av, $bv);
117
                if ($comp != 0) {
118
                    return $comp;
119
                }
120
            }
121
        }
122
        if (key($b) !== null) {
123
            return -1;
124
        }
125
126
        // ties broken by the subscripts of the first item
127
        $aFst = $a;
128
        $bFst = $b;
129
        do {
130
            reset($aFst);
131
            reset($bFst);
132
            $ak = key($aFst);
133
            $bk = key($bFst);
134
            if ($ak === null && $bk === null) {
135
                return 0;
136
            } elseif ($ak === null) {
137
                return -1;
138
            } elseif ($bk === null) {
139
                return 1;
140
            } elseif (!is_numeric($ak) || !is_numeric($bk)) {
141
                return 0;
142
            } else {
143
                $d = $ak - $bk;
144
                if ($d) {
145
                    return $d;
146
                } else {
147
                    $aFst = current($aFst);
148
                    $bFst = current($bFst);
149
                }
150
            }
151
        } while (is_array($aFst) && is_array($bFst));
152
153
        return 0;
154
    }
155
}
156