• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
18 #pragma once
19 
20 #include <math.h>
21 #include <stdint.h>
22 #include <math/HashCombine.h>
23 #include <sys/types.h>
24 
25 #include <cmath>
26 #include <functional>
27 #include <limits>
28 #include <iostream>
29 
30 #define PURE __attribute__((pure))
31 
32 #if __cplusplus >= 201402L
33 #define CONSTEXPR constexpr
34 #else
35 #define CONSTEXPR
36 #endif
37 
38 namespace android {
39 namespace details {
40 // -------------------------------------------------------------------------------------
41 
42 /*
43  * No user serviceable parts here.
44  *
45  * Don't use this file directly, instead include ui/vec{2|3|4}.h
46  */
47 
48 /*
49  * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments
50  * operators on a vector of type BASE<T>.
51  *
52  * BASE only needs to implement operator[] and size().
53  * By simply inheriting from TVec{Add|Product}Operators<BASE, T> BASE will automatically
54  * get all the functionality here.
55  */
56 
57 template <template<typename T> class VECTOR, typename T>
58 class TVecAddOperators {
59 public:
60     /* compound assignment from a another vector of the same size but different
61      * element type.
62      */
63     template<typename OTHER>
64     VECTOR<T>& operator +=(const VECTOR<OTHER>& v) {
65         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
66         for (size_t i = 0; i < lhs.size(); i++) {
67             lhs[i] += v[i];
68         }
69         return lhs;
70     }
71     template<typename OTHER>
72     VECTOR<T>& operator -=(const VECTOR<OTHER>& v) {
73         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
74         for (size_t i = 0; i < lhs.size(); i++) {
75             lhs[i] -= v[i];
76         }
77         return lhs;
78     }
79 
80     /* compound assignment from a another vector of the same type.
81      * These operators can be used for implicit conversion and  handle operations
82      * like "vector *= scalar" by letting the compiler implicitly convert a scalar
83      * to a vector (assuming the BASE<T> allows it).
84      */
85     VECTOR<T>& operator +=(const VECTOR<T>& v) {
86         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
87         for (size_t i = 0; i < lhs.size(); i++) {
88             lhs[i] += v[i];
89         }
90         return lhs;
91     }
92     VECTOR<T>& operator -=(const VECTOR<T>& v) {
93         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
94         for (size_t i = 0; i < lhs.size(); i++) {
95             lhs[i] -= v[i];
96         }
97         return lhs;
98     }
99 
100     /*
101      * NOTE: the functions below ARE NOT member methods. They are friend functions
102      * with they definition inlined with their declaration. This makes these
103      * template functions available to the compiler when (and only when) this class
104      * is instantiated, at which point they're only templated on the 2nd parameter
105      * (the first one, BASE<T> being known).
106      */
107 
108     /* The operators below handle operation between vectors of the same size
109      * but of a different element type.
110      */
111     template<typename RT>
112     friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<RT>& rv) {
113         // don't pass lv by reference because we need a copy anyways
114         return lv += rv;
115     }
116     template<typename RT>
117     friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<RT>& rv) {
118         // don't pass lv by reference because we need a copy anyways
119         return lv -= rv;
120     }
121 
122     /* The operators below (which are not templates once this class is instanced,
123      * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
124      * These handle operations like "vector + scalar" and "scalar + vector" by
125      * letting the compiler implicitly convert a scalar to a vector (assuming
126      * the BASE<T> allows it).
127      */
128     friend inline constexpr VECTOR<T> PURE operator +(VECTOR<T> lv, const VECTOR<T>& rv) {
129         // don't pass lv by reference because we need a copy anyways
130         return lv += rv;
131     }
132     friend inline constexpr VECTOR<T> PURE operator -(VECTOR<T> lv, const VECTOR<T>& rv) {
133         // don't pass lv by reference because we need a copy anyways
134         return lv -= rv;
135     }
136 };
137 
138 template<template<typename T> class VECTOR, typename T>
139 class TVecProductOperators {
140 public:
141     /* compound assignment from a another vector of the same size but different
142      * element type.
143      */
144     template<typename OTHER>
145     VECTOR<T>& operator *=(const VECTOR<OTHER>& v) {
146         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
147         for (size_t i = 0; i < lhs.size(); i++) {
148             lhs[i] *= v[i];
149         }
150         return lhs;
151     }
152     template<typename OTHER>
153     VECTOR<T>& operator /=(const VECTOR<OTHER>& v) {
154         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
155         for (size_t i = 0; i < lhs.size(); i++) {
156             lhs[i] /= v[i];
157         }
158         return lhs;
159     }
160 
161     /* compound assignment from a another vector of the same type.
162      * These operators can be used for implicit conversion and  handle operations
163      * like "vector *= scalar" by letting the compiler implicitly convert a scalar
164      * to a vector (assuming the BASE<T> allows it).
165      */
166     VECTOR<T>& operator *=(const VECTOR<T>& v) {
167         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
168         for (size_t i = 0; i < lhs.size(); i++) {
169             lhs[i] *= v[i];
170         }
171         return lhs;
172     }
173     VECTOR<T>& operator /=(const VECTOR<T>& v) {
174         VECTOR<T>& lhs = static_cast<VECTOR<T>&>(*this);
175         for (size_t i = 0; i < lhs.size(); i++) {
176             lhs[i] /= v[i];
177         }
178         return lhs;
179     }
180 
181     /*
182      * NOTE: the functions below ARE NOT member methods. They are friend functions
183      * with they definition inlined with their declaration. This makes these
184      * template functions available to the compiler when (and only when) this class
185      * is instantiated, at which point they're only templated on the 2nd parameter
186      * (the first one, BASE<T> being known).
187      */
188 
189     /* The operators below handle operation between vectors of the same size
190      * but of a different element type.
191      */
192     template<typename RT>
193     friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<RT>& rv) {
194         // don't pass lv by reference because we need a copy anyways
195         return lv *= rv;
196     }
197     template<typename RT>
198     friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<RT>& rv) {
199         // don't pass lv by reference because we need a copy anyways
200         return lv /= rv;
201     }
202 
203     /* The operators below (which are not templates once this class is instanced,
204      * i.e.: BASE<T> is known) can be used for implicit conversion on both sides.
205      * These handle operations like "vector * scalar" and "scalar * vector" by
206      * letting the compiler implicitly convert a scalar to a vector (assuming
207      * the BASE<T> allows it).
208      */
209     friend inline constexpr VECTOR<T> PURE operator *(VECTOR<T> lv, const VECTOR<T>& rv) {
210         // don't pass lv by reference because we need a copy anyways
211         return lv *= rv;
212     }
213     friend inline constexpr VECTOR<T> PURE operator /(VECTOR<T> lv, const VECTOR<T>& rv) {
214         // don't pass lv by reference because we need a copy anyways
215         return lv /= rv;
216     }
217 };
218 
219 /*
220  * TVecUnaryOperators implements unary operators on a vector of type BASE<T>.
221  *
222  * BASE only needs to implement operator[] and size().
223  * By simply inheriting from TVecUnaryOperators<BASE, T> BASE will automatically
224  * get all the functionality here.
225  *
226  * These operators are implemented as friend functions of TVecUnaryOperators<BASE, T>
227  */
228 template<template<typename T> class VECTOR, typename T>
229 class TVecUnaryOperators {
230 public:
231     VECTOR<T>& operator ++() {
232         VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
233         for (size_t i = 0; i < rhs.size(); i++) {
234             ++rhs[i];
235         }
236         return rhs;
237     }
238 
239     VECTOR<T>& operator --() {
240         VECTOR<T>& rhs = static_cast<VECTOR<T>&>(*this);
241         for (size_t i = 0; i < rhs.size(); i++) {
242             --rhs[i];
243         }
244         return rhs;
245     }
246 
247     CONSTEXPR VECTOR<T> operator -() const {
248         VECTOR<T> r(VECTOR<T>::NO_INIT);
249         VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
250         for (size_t i = 0; i < r.size(); i++) {
251             r[i] = -rv[i];
252         }
253         return r;
254     }
255 
256     // This isn't strictly a unary operator, but it is a common place shared between both
257     // matrix and vector classes
hash()258     size_t hash() const {
259         VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
260         size_t hashed = 0;
261         for (size_t i = 0; i < rv.size(); i++) {
262             android::hashCombineSingle(hashed, rv[i]);
263         }
264         return hashed;
265     }
266 };
267 
268 /*
269  * TVecComparisonOperators implements relational/comparison operators
270  * on a vector of type BASE<T>.
271  *
272  * BASE only needs to implement operator[] and size().
273  * By simply inheriting from TVecComparisonOperators<BASE, T> BASE will automatically
274  * get all the functionality here.
275  */
276 template<template<typename T> class VECTOR, typename T>
277 class TVecComparisonOperators {
278 public:
279     /*
280      * NOTE: the functions below ARE NOT member methods. They are friend functions
281      * with they definition inlined with their declaration. This makes these
282      * template functions available to the compiler when (and only when) this class
283      * is instantiated, at which point they're only templated on the 2nd parameter
284      * (the first one, BASE<T> being known).
285      */
286     template<typename RT>
287     friend inline
288     bool PURE operator ==(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
289         for (size_t i = 0; i < lv.size(); i++)
290             if (lv[i] != rv[i])
291                 return false;
292         return true;
293     }
294 
295     template<typename RT>
296     friend inline
297     bool PURE operator !=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
298         return !operator ==(lv, rv);
299     }
300 
301     template<typename RT>
302     friend inline
303     bool PURE operator >(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
304         for (size_t i = 0; i < lv.size(); i++) {
305             if (lv[i] == rv[i]) {
306                 continue;
307             }
308             return lv[i] > rv[i];
309         }
310         return false;
311     }
312 
313     template<typename RT>
314     friend inline
315     constexpr bool PURE operator <=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
316         return !(lv > rv);
317     }
318 
319     template<typename RT>
320     friend inline
321     bool PURE operator <(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
322         for (size_t i = 0; i < lv.size(); i++) {
323             if (lv[i] == rv[i]) {
324                 continue;
325             }
326             return lv[i] < rv[i];
327         }
328         return false;
329     }
330 
331     template<typename RT>
332     friend inline
333     constexpr bool PURE operator >=(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
334         return !(lv < rv);
335     }
336 
337     template<typename RT>
338     friend inline
equal(const VECTOR<T> & lv,const VECTOR<RT> & rv)339     CONSTEXPR VECTOR<bool> PURE equal(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
340         VECTOR<bool> r;
341         for (size_t i = 0; i < lv.size(); i++) {
342             r[i] = lv[i] == rv[i];
343         }
344         return r;
345     }
346 
347     template<typename RT>
348     friend inline
notEqual(const VECTOR<T> & lv,const VECTOR<RT> & rv)349     CONSTEXPR VECTOR<bool> PURE notEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
350         VECTOR<bool> r;
351         for (size_t i = 0; i < lv.size(); i++) {
352             r[i] = lv[i] != rv[i];
353         }
354         return r;
355     }
356 
357     template<typename RT>
358     friend inline
lessThan(const VECTOR<T> & lv,const VECTOR<RT> & rv)359     CONSTEXPR VECTOR<bool> PURE lessThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
360         VECTOR<bool> r;
361         for (size_t i = 0; i < lv.size(); i++) {
362             r[i] = lv[i] < rv[i];
363         }
364         return r;
365     }
366 
367     template<typename RT>
368     friend inline
lessThanEqual(const VECTOR<T> & lv,const VECTOR<RT> & rv)369     CONSTEXPR VECTOR<bool> PURE lessThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
370         VECTOR<bool> r;
371         for (size_t i = 0; i < lv.size(); i++) {
372             r[i] = lv[i] <= rv[i];
373         }
374         return r;
375     }
376 
377     template<typename RT>
378     friend inline
greaterThan(const VECTOR<T> & lv,const VECTOR<RT> & rv)379     CONSTEXPR VECTOR<bool> PURE greaterThan(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
380         VECTOR<bool> r;
381         for (size_t i = 0; i < lv.size(); i++) {
382             r[i] = lv[i] > rv[i];
383         }
384         return r;
385     }
386 
387     template<typename RT>
388     friend inline
greaterThanEqual(const VECTOR<T> & lv,const VECTOR<RT> & rv)389     CONSTEXPR VECTOR<bool> PURE greaterThanEqual(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
390         VECTOR<bool> r;
391         for (size_t i = 0; i < lv.size(); i++) {
392             r[i] = lv[i] >= rv[i];
393         }
394         return r;
395     }
396 };
397 
398 /*
399  * TVecFunctions implements functions on a vector of type BASE<T>.
400  *
401  * BASE only needs to implement operator[] and size().
402  * By simply inheriting from TVecFunctions<BASE, T> BASE will automatically
403  * get all the functionality here.
404  */
405 template<template<typename T> class VECTOR, typename T>
406 class TVecFunctions {
407 public:
408     /*
409      * NOTE: the functions below ARE NOT member methods. They are friend functions
410      * with they definition inlined with their declaration. This makes these
411      * template functions available to the compiler when (and only when) this class
412      * is instantiated, at which point they're only templated on the 2nd parameter
413      * (the first one, BASE<T> being known).
414      */
415     template<typename RT>
dot(const VECTOR<T> & lv,const VECTOR<RT> & rv)416     friend inline CONSTEXPR T PURE dot(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
417         T r(0);
418         for (size_t i = 0; i < lv.size(); i++) {
419             //r = std::fma(lv[i], rv[i], r);
420             r += lv[i] * rv[i];
421         }
422         return r;
423     }
424 
norm(const VECTOR<T> & lv)425     friend inline constexpr T PURE norm(const VECTOR<T>& lv) {
426         return std::sqrt(dot(lv, lv));
427     }
428 
length(const VECTOR<T> & lv)429     friend inline constexpr T PURE length(const VECTOR<T>& lv) {
430         return norm(lv);
431     }
432 
norm2(const VECTOR<T> & lv)433     friend inline constexpr T PURE norm2(const VECTOR<T>& lv) {
434         return dot(lv, lv);
435     }
436 
length2(const VECTOR<T> & lv)437     friend inline constexpr T PURE length2(const VECTOR<T>& lv) {
438         return norm2(lv);
439     }
440 
441     template<typename RT>
distance(const VECTOR<T> & lv,const VECTOR<RT> & rv)442     friend inline constexpr T PURE distance(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
443         return length(rv - lv);
444     }
445 
446     template<typename RT>
distance2(const VECTOR<T> & lv,const VECTOR<RT> & rv)447     friend inline constexpr T PURE distance2(const VECTOR<T>& lv, const VECTOR<RT>& rv) {
448         return length2(rv - lv);
449     }
450 
normalize(const VECTOR<T> & lv)451     friend inline constexpr VECTOR<T> PURE normalize(const VECTOR<T>& lv) {
452         return lv * (T(1) / length(lv));
453     }
454 
rcp(VECTOR<T> v)455     friend inline constexpr VECTOR<T> PURE rcp(VECTOR<T> v) {
456         return T(1) / v;
457     }
458 
abs(VECTOR<T> v)459     friend inline CONSTEXPR VECTOR<T> PURE abs(VECTOR<T> v) {
460         for (size_t i = 0; i < v.size(); i++) {
461             v[i] = std::abs(v[i]);
462         }
463         return v;
464     }
465 
floor(VECTOR<T> v)466     friend inline CONSTEXPR VECTOR<T> PURE floor(VECTOR<T> v) {
467         for (size_t i = 0; i < v.size(); i++) {
468             v[i] = std::floor(v[i]);
469         }
470         return v;
471     }
472 
ceil(VECTOR<T> v)473     friend inline CONSTEXPR VECTOR<T> PURE ceil(VECTOR<T> v) {
474         for (size_t i = 0; i < v.size(); i++) {
475             v[i] = std::ceil(v[i]);
476         }
477         return v;
478     }
479 
round(VECTOR<T> v)480     friend inline CONSTEXPR VECTOR<T> PURE round(VECTOR<T> v) {
481         for (size_t i = 0; i < v.size(); i++) {
482             v[i] = std::round(v[i]);
483         }
484         return v;
485     }
486 
inversesqrt(VECTOR<T> v)487     friend inline CONSTEXPR VECTOR<T> PURE inversesqrt(VECTOR<T> v) {
488         for (size_t i = 0; i < v.size(); i++) {
489             v[i] = T(1) / std::sqrt(v[i]);
490         }
491         return v;
492     }
493 
sqrt(VECTOR<T> v)494     friend inline CONSTEXPR VECTOR<T> PURE sqrt(VECTOR<T> v) {
495         for (size_t i = 0; i < v.size(); i++) {
496             v[i] = std::sqrt(v[i]);
497         }
498         return v;
499     }
500 
pow(VECTOR<T> v,T p)501     friend inline CONSTEXPR VECTOR<T> PURE pow(VECTOR<T> v, T p) {
502         for (size_t i = 0; i < v.size(); i++) {
503             v[i] = std::pow(v[i], p);
504         }
505         return v;
506     }
507 
saturate(const VECTOR<T> & lv)508     friend inline CONSTEXPR VECTOR<T> PURE saturate(const VECTOR<T>& lv) {
509         return clamp(lv, T(0), T(1));
510     }
511 
clamp(VECTOR<T> v,T min,T max)512     friend inline CONSTEXPR VECTOR<T> PURE clamp(VECTOR<T> v, T min, T max) {
513         for (size_t i = 0; i< v.size(); i++) {
514             v[i] = std::min(max, std::max(min, v[i]));
515         }
516         return v;
517     }
518 
fma(const VECTOR<T> & lv,const VECTOR<T> & rv,VECTOR<T> a)519     friend inline CONSTEXPR VECTOR<T> PURE fma(const VECTOR<T>& lv, const VECTOR<T>& rv, VECTOR<T> a) {
520         for (size_t i = 0; i<lv.size(); i++) {
521             //a[i] = std::fma(lv[i], rv[i], a[i]);
522             a[i] += (lv[i] * rv[i]);
523         }
524         return a;
525     }
526 
min(const VECTOR<T> & u,VECTOR<T> v)527     friend inline CONSTEXPR VECTOR<T> PURE min(const VECTOR<T>& u, VECTOR<T> v) {
528         for (size_t i = 0; i < v.size(); i++) {
529             v[i] = std::min(u[i], v[i]);
530         }
531         return v;
532     }
533 
max(const VECTOR<T> & u,VECTOR<T> v)534     friend inline CONSTEXPR VECTOR<T> PURE max(const VECTOR<T>& u, VECTOR<T> v) {
535         for (size_t i = 0; i < v.size(); i++) {
536             v[i] = std::max(u[i], v[i]);
537         }
538         return v;
539     }
540 
max(const VECTOR<T> & v)541     friend inline CONSTEXPR T PURE max(const VECTOR<T>& v) {
542         T r(std::numeric_limits<T>::lowest());
543         for (size_t i = 0; i < v.size(); i++) {
544             r = std::max(r, v[i]);
545         }
546         return r;
547     }
548 
min(const VECTOR<T> & v)549     friend inline CONSTEXPR T PURE min(const VECTOR<T>& v) {
550         T r(std::numeric_limits<T>::max());
551         for (size_t i = 0; i < v.size(); i++) {
552             r = std::min(r, v[i]);
553         }
554         return r;
555     }
556 
apply(VECTOR<T> v,const std::function<T (T)> & f)557     friend inline CONSTEXPR VECTOR<T> PURE apply(VECTOR<T> v, const std::function<T(T)>& f) {
558         for (size_t i = 0; i < v.size(); i++) {
559             v[i] = f(v[i]);
560         }
561         return v;
562     }
563 
any(const VECTOR<T> & v)564     friend inline CONSTEXPR bool PURE any(const VECTOR<T>& v) {
565         for (size_t i = 0; i < v.size(); i++) {
566             if (v[i] != T(0)) return true;
567         }
568         return false;
569     }
570 
all(const VECTOR<T> & v)571     friend inline CONSTEXPR bool PURE all(const VECTOR<T>& v) {
572         bool result = true;
573         for (size_t i = 0; i < v.size(); i++) {
574             result &= (v[i] != T(0));
575         }
576         return result;
577     }
578 
579     template<typename R>
map(VECTOR<T> v,const std::function<R (T)> & f)580     friend inline CONSTEXPR VECTOR<R> PURE map(VECTOR<T> v, const std::function<R(T)>& f) {
581         VECTOR<R> result;
582         for (size_t i = 0; i < v.size(); i++) {
583             result[i] = f(v[i]);
584         }
585         return result;
586     }
587 };
588 
589 /*
590  * TVecDebug implements functions on a vector of type BASE<T>.
591  *
592  * BASE only needs to implement operator[] and size().
593  * By simply inheriting from TVecDebug<BASE, T> BASE will automatically
594  * get all the functionality here.
595  */
596 template<template<typename T> class VECTOR, typename T>
597 class TVecDebug {
598 public:
599     /*
600      * NOTE: the functions below ARE NOT member methods. They are friend functions
601      * with they definition inlined with their declaration. This makes these
602      * template functions available to the compiler when (and only when) this class
603      * is instantiated, at which point they're only templated on the 2nd parameter
604      * (the first one, BASE<T> being known).
605      */
606     friend std::ostream& operator<<(std::ostream& stream, const VECTOR<T>& v) {
607         stream << "< ";
608         for (size_t i = 0; i < v.size() - 1; i++) {
609             stream << T(v[i]) << ", ";
610         }
611         stream << T(v[v.size() - 1]) << " >";
612         return stream;
613     }
614 };
615 
616 #undef CONSTEXPR
617 #undef PURE
618 
619 // -------------------------------------------------------------------------------------
620 }  // namespace details
621 }  // namespace android
622 
623 namespace std {
624     template<template<typename T> class VECTOR, typename T>
625     struct hash<VECTOR<T>> {
626         static constexpr bool IS_VECTOR =
627             std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
628 
629         typename std::enable_if<IS_VECTOR, size_t>::type
630         operator()(const VECTOR<T>& v) const {
631             return v.hash();
632         }
633     };
634 }
635