1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 // appleseed.foundation headers.
31 #include "foundation/math/combination.h"
32 #include "foundation/math/fp.h"
33 #include "foundation/math/minmax.h"
34 #ifdef APPLESEED_USE_SSE
35 #include "foundation/platform/sse.h"
36 #endif
37 #include "foundation/utility/casts.h"
38 #include "foundation/utility/test.h"
39 #include "foundation/utility/typetraits.h"
40 
41 // Standard headers.
42 #include <algorithm>
43 #include <cstddef>
44 #include <utility>
45 
46 using namespace foundation;
47 using namespace std;
48 
TEST_SUITE(Foundation_Math_MinMax)49 TEST_SUITE(Foundation_Math_MinMax)
50 {
51     TEST_CASE(Min_ThreeArguments)
52     {
53         EXPECT_EQ(0, min(0, 1, 2));
54         EXPECT_EQ(0, min(0, 2, 1));
55         EXPECT_EQ(0, min(1, 0, 2));
56         EXPECT_EQ(0, min(1, 2, 0));
57         EXPECT_EQ(0, min(2, 0, 1));
58         EXPECT_EQ(0, min(2, 1, 0));
59     }
60 
61     TEST_CASE(Max_ThreeArguments)
62     {
63         EXPECT_EQ(2, max(0, 1, 2));
64         EXPECT_EQ(2, max(0, 2, 1));
65         EXPECT_EQ(2, max(1, 0, 2));
66         EXPECT_EQ(2, max(1, 2, 0));
67         EXPECT_EQ(2, max(2, 0, 1));
68         EXPECT_EQ(2, max(2, 1, 0));
69     }
70 
71     pair<int, int> minmax_pair(const int a, const int b)
72     {
73         int min, max;
74         minmax(a, b, min, max);
75         return make_pair(min, max);
76     }
77 
78     pair<int, int> minmax_pair(const int a, const int b, const int c)
79     {
80         int min, max;
81         minmax(a, b, c, min, max);
82         return make_pair(min, max);
83     }
84 
85     TEST_CASE(MinMax_TwoArguments)
86     {
87         EXPECT_EQ(0, minmax_pair(0, 2).first);
88         EXPECT_EQ(2, minmax_pair(0, 2).second);
89         EXPECT_EQ(0, minmax_pair(2, 0).first);
90         EXPECT_EQ(2, minmax_pair(2, 0).second);
91     }
92 
93     TEST_CASE(MinMax_ThreeArguments)
94     {
95         EXPECT_EQ(0, minmax_pair(0, 1, 2).first);
96         EXPECT_EQ(2, minmax_pair(0, 1, 2).second);
97         EXPECT_EQ(0, minmax_pair(0, 2, 1).first);
98         EXPECT_EQ(2, minmax_pair(0, 2, 1).second);
99         EXPECT_EQ(0, minmax_pair(1, 0, 2).first);
100         EXPECT_EQ(2, minmax_pair(1, 0, 2).second);
101         EXPECT_EQ(0, minmax_pair(1, 2, 0).first);
102         EXPECT_EQ(2, minmax_pair(1, 2, 0).second);
103         EXPECT_EQ(0, minmax_pair(2, 0, 1).first);
104         EXPECT_EQ(2, minmax_pair(2, 0, 1).second);
105         EXPECT_EQ(0, minmax_pair(2, 1, 0).first);
106         EXPECT_EQ(2, minmax_pair(2, 1, 0).second);
107     }
108 
109 #ifdef APPLESEED_USE_SSE
110 
111     float ssemin_reference(const float a, const float b)
112     {
113         return _mm_cvtss_f32(_mm_min_ss(_mm_set_ss(a), _mm_set_ss(b)));
114     }
115 
116     float ssemax_reference(const float a, const float b)
117     {
118         return _mm_cvtss_f32(_mm_max_ss(_mm_set_ss(a), _mm_set_ss(b)));
119     }
120 
121     TEST_CASE(SSEMinAndSSEMax_GivenPermutationsOfSpecialFloatingPointNumbers_MatchMINSSAndMAXSS)
122     {
123         typedef TypeConv<float>::UInt UInt;
124 
125         static UInt Values[] =
126         {
127             binary_cast<UInt>(1.0f),
128             binary_cast<UInt>(2.0f),
129             binary_cast<UInt>(FP<float>::pos_zero()),
130             binary_cast<UInt>(FP<float>::neg_zero()),
131             binary_cast<UInt>(FP<float>::pos_min()),
132             binary_cast<UInt>(FP<float>::neg_min()),
133             binary_cast<UInt>(FP<float>::pos_inf()),
134             binary_cast<UInt>(FP<float>::neg_inf()),
135             binary_cast<UInt>(FP<float>::snan()),
136             binary_cast<UInt>(FP<float>::qnan())
137         };
138 
139         const size_t N = countof(Values);
140 
141         sort(&Values[0], &Values[N]);
142 
143         UInt* first = &Values[0];
144         UInt* middle = &Values[2];
145         UInt* last = &Values[N];
146 
147         size_t permutation_count = 0;
148 
149         do
150         {
151             const float a = binary_cast<float>(*first);
152             const float b = binary_cast<float>(*middle);
153 
154             const UInt expected_min = binary_cast<UInt>(ssemin_reference(a, b));
155             const UInt obtained_min = binary_cast<UInt>(ssemin(a, b));
156 
157             EXPECT_EQ(expected_min, obtained_min);
158 
159             const UInt expected_max = binary_cast<UInt>(ssemax_reference(a, b));
160             const UInt obtained_max = binary_cast<UInt>(ssemax(a, b));
161 
162             EXPECT_EQ(expected_max, obtained_max);
163 
164             ++permutation_count;
165         }
166         while (next_partial_permutation(first, middle, last));
167 
168         const size_t ExpectedPermutationCount = factorial(N) / factorial(N - 2);
169 
170         EXPECT_EQ(ExpectedPermutationCount, permutation_count);
171     }
172 
173 #endif  // APPLESEED_USE_SSE
174 
175     TEST_CASE(MinIndex_TwoArguments)
176     {
177         EXPECT_EQ(0, min_index(0, 2));
178         EXPECT_EQ(1, min_index(2, 0));
179     }
180 
181     TEST_CASE(MinIndex_ThreeArguments)
182     {
183         EXPECT_EQ(0, min_index(0, 1, 2));
184         EXPECT_EQ(0, min_index(0, 2, 1));
185         EXPECT_EQ(1, min_index(1, 0, 2));
186         EXPECT_EQ(2, min_index(1, 2, 0));
187         EXPECT_EQ(1, min_index(2, 0, 1));
188         EXPECT_EQ(2, min_index(2, 1, 0));
189     }
190 
191     TEST_CASE(MaxIndex_TwoArguments)
192     {
193         EXPECT_EQ(1, max_index(0, 2));
194         EXPECT_EQ(0, max_index(2, 0));
195     }
196 
197     TEST_CASE(MaxIndex_ThreeArguments)
198     {
199         EXPECT_EQ(2, max_index(0, 1, 2));
200         EXPECT_EQ(1, max_index(0, 2, 1));
201         EXPECT_EQ(2, max_index(1, 0, 2));
202         EXPECT_EQ(1, max_index(1, 2, 0));
203         EXPECT_EQ(0, max_index(2, 0, 1));
204         EXPECT_EQ(0, max_index(2, 1, 0));
205     }
206 }
207