xref: /libCEED/rust/libceed-sys/c-src/backends/ref/ceed-ref-restriction.c (revision 523b8ea09bebe65e72854bf0f9327b4f4d8bccdb)
121617c04Sjeremylt // Copyright (c) 2017-2018, Lawrence Livermore National Security, LLC.
221617c04Sjeremylt // Produced at the Lawrence Livermore National Laboratory. LLNL-CODE-734707.
321617c04Sjeremylt // All Rights reserved. See files LICENSE and NOTICE for details.
421617c04Sjeremylt //
521617c04Sjeremylt // This file is part of CEED, a collection of benchmarks, miniapps, software
621617c04Sjeremylt // libraries and APIs for efficient high-order finite element and spectral
721617c04Sjeremylt // element discretizations for exascale applications. For more information and
821617c04Sjeremylt // source code availability see http://github.com/ceed.
921617c04Sjeremylt //
1021617c04Sjeremylt // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
1121617c04Sjeremylt // a collaborative effort of two U.S. Department of Energy organizations (Office
1221617c04Sjeremylt // of Science and the National Nuclear Security Administration) responsible for
1321617c04Sjeremylt // the planning and preparation of a capable exascale ecosystem, including
1421617c04Sjeremylt // software, applications, hardware, advanced system engineering and early
1521617c04Sjeremylt // testbed platforms, in support of the nation's exascale computing imperative.
1621617c04Sjeremylt 
1721617c04Sjeremylt #include "ceed-ref.h"
1821617c04Sjeremylt 
19f10650afSjeremylt //------------------------------------------------------------------------------
20f10650afSjeremylt // Core ElemRestriction Apply Code
21f10650afSjeremylt //------------------------------------------------------------------------------
22be9261b7Sjeremylt static inline int CeedElemRestrictionApply_Ref_Core(CeedElemRestriction r,
239c36149bSjeremylt     const CeedInt blksize, const CeedInt ncomp, CeedInt start, CeedInt stop,
2461dbc9d2Sjeremylt     CeedTransposeMode tmode, CeedInterlaceMode imode, CeedVector u,
259c36149bSjeremylt     CeedVector v, CeedRequest *request) {
2621617c04Sjeremylt   int ierr;
274ce2993fSjeremylt   CeedElemRestriction_Ref *impl;
287509a596Sjeremylt   ierr = CeedElemRestrictionGetData(r, (void *)&impl); CeedChk(ierr);
2921617c04Sjeremylt   const CeedScalar *uu;
3021617c04Sjeremylt   CeedScalar *vv;
319c36149bSjeremylt   CeedInt nelem, elemsize, nnodes, voffset;
324ce2993fSjeremylt   ierr = CeedElemRestrictionGetNumElements(r, &nelem); CeedChk(ierr);
334ce2993fSjeremylt   ierr = CeedElemRestrictionGetElementSize(r, &elemsize); CeedChk(ierr);
348795c945Sjeremylt   ierr = CeedElemRestrictionGetNumNodes(r, &nnodes); CeedChk(ierr);
35be9261b7Sjeremylt   voffset = start*blksize*elemsize*ncomp;
3621617c04Sjeremylt 
3721617c04Sjeremylt   ierr = CeedVectorGetArrayRead(u, CEED_MEM_HOST, &uu); CeedChk(ierr);
3821617c04Sjeremylt   ierr = CeedVectorGetArray(v, CEED_MEM_HOST, &vv); CeedChk(ierr);
398d94b059Sjeremylt   // Restriction from lvector to evector
4021617c04Sjeremylt   // Perform: v = r * u
418d94b059Sjeremylt   if (tmode == CEED_NOTRANSPOSE) {
428c91a0c9SJeremy L Thompson     // No indices provided, Identity Restriction
43e17b31afSThilina Rathnayake     if (!impl->indices) {
447509a596Sjeremylt       CeedInt strides[3];
457509a596Sjeremylt       ierr = CeedElemRestrictionGetStrides(r, &strides); CeedChk(ierr);
46*523b8ea0Sjeremylt       bool backendstrides;
47*523b8ea0Sjeremylt       ierr = CeedElemRestrictionGetBackendStridesStatus(r, &backendstrides);
48*523b8ea0Sjeremylt       CeedChk(ierr);
49*523b8ea0Sjeremylt       if (backendstrides) {
50*523b8ea0Sjeremylt         for (CeedInt e = start*blksize; e < stop*blksize; e+=blksize)
51*523b8ea0Sjeremylt           CeedPragmaSIMD
52*523b8ea0Sjeremylt           for (CeedInt k = 0; k < ncomp*elemsize; k++)
53*523b8ea0Sjeremylt             CeedPragmaSIMD
54*523b8ea0Sjeremylt             for (CeedInt j = 0; j < blksize; j++) {
55*523b8ea0Sjeremylt               vv[e*elemsize*ncomp + k*blksize + j - voffset] =
56*523b8ea0Sjeremylt                 uu[e*elemsize*ncomp + k*CeedIntMin(blksize, nelem-e) +
57*523b8ea0Sjeremylt                    CeedIntMin(j, nelem-e)];
58*523b8ea0Sjeremylt           }
59*523b8ea0Sjeremylt       } else {
60be9261b7Sjeremylt         for (CeedInt e = start*blksize; e < stop*blksize; e+=blksize)
61e1b98f6eSjeremylt           CeedPragmaSIMD
627509a596Sjeremylt           for (CeedInt k = 0; k < ncomp; k++)
631d79ecccSjeremylt             CeedPragmaSIMD
647509a596Sjeremylt             for (CeedInt n = 0; n < elemsize; n++)
657509a596Sjeremylt               CeedPragmaSIMD
667509a596Sjeremylt               for (CeedInt j = 0; j < blksize; j++) {
677509a596Sjeremylt                 vv[e*elemsize*ncomp + (k*elemsize+n)*blksize + j - voffset]
687509a596Sjeremylt                   = uu[n*strides[0] + k*strides[1] +
697509a596Sjeremylt                                     CeedIntMin(e+j, nelem-1)*strides[2]];
707509a596Sjeremylt             }
71*523b8ea0Sjeremylt       }
7221617c04Sjeremylt     } else {
738c91a0c9SJeremy L Thompson       // Indices provided, standard or blocked restriction
74ecf6354eSJed Brown       // vv has shape [elemsize, ncomp, nelem], row-major
758795c945Sjeremylt       // uu has shape [nnodes, ncomp]
76be9261b7Sjeremylt       for (CeedInt e = start*blksize; e < stop*blksize; e+=blksize)
77e1b98f6eSjeremylt         CeedPragmaSIMD
7821617c04Sjeremylt         for (CeedInt d = 0; d < ncomp; d++)
79e1b98f6eSjeremylt           CeedPragmaSIMD
8006cdd269SJed Brown           for (CeedInt i = 0; i < elemsize*blksize; i++)
811d79ecccSjeremylt             vv[elemsize*(d*blksize+ncomp*e) + i - voffset]
8261dbc9d2Sjeremylt               = uu[imode == CEED_NONINTERLACED
838795c945Sjeremylt                          ? impl->indices[i+elemsize*e]+nnodes*d
8406cdd269SJed Brown                          : d+ncomp*impl->indices[i+elemsize*e]];
8521617c04Sjeremylt     }
8621617c04Sjeremylt   } else {
878d94b059Sjeremylt     // Restriction from evector to lvector
888d94b059Sjeremylt     // Performing v += r^T * u
898c91a0c9SJeremy L Thompson     // No indices provided, Identity Restriction
90e17b31afSThilina Rathnayake     if (!impl->indices) {
917509a596Sjeremylt       CeedInt strides[3];
927509a596Sjeremylt       ierr = CeedElemRestrictionGetStrides(r, &strides); CeedChk(ierr);
93*523b8ea0Sjeremylt       bool backendstrides;
94*523b8ea0Sjeremylt       ierr = CeedElemRestrictionGetBackendStridesStatus(r, &backendstrides);
95*523b8ea0Sjeremylt       CeedChk(ierr);
96*523b8ea0Sjeremylt       if (backendstrides) {
97*523b8ea0Sjeremylt         for (CeedInt e = start*blksize; e < stop*blksize; e+=blksize)
98*523b8ea0Sjeremylt           CeedPragmaSIMD
99*523b8ea0Sjeremylt           for (CeedInt k = 0; k < ncomp*elemsize; k++)
100*523b8ea0Sjeremylt             CeedPragmaSIMD
101*523b8ea0Sjeremylt             for (CeedInt j = 0; j < CeedIntMin(blksize, nelem-e); j++)
102*523b8ea0Sjeremylt               vv[e*elemsize*ncomp + k*CeedIntMin(blksize, nelem-e) + j]
103*523b8ea0Sjeremylt               += uu[e*elemsize*ncomp + k*blksize + j - voffset];
104*523b8ea0Sjeremylt       } else {
105be9261b7Sjeremylt         for (CeedInt e = start*blksize; e < stop*blksize; e+=blksize)
1064ce2993fSjeremylt           for (CeedInt j = 0; j < CeedIntMin(blksize, nelem-e); j++)
1071d79ecccSjeremylt             CeedPragmaSIMD
1087509a596Sjeremylt             for (CeedInt k = 0; k < ncomp; k++)
1097509a596Sjeremylt               CeedPragmaSIMD
1107509a596Sjeremylt               for (CeedInt n = 0; n < elemsize; n++)
1117509a596Sjeremylt                 vv[n*strides[0] + k*strides[1] + (e+j)*strides[2]]
1127509a596Sjeremylt                 += uu[e*elemsize*ncomp + (k*elemsize+n)*blksize + j - voffset];
113*523b8ea0Sjeremylt       }
11421617c04Sjeremylt     } else {
1158c91a0c9SJeremy L Thompson       // Indices provided, standard or blocked restriction
116ecf6354eSJed Brown       // uu has shape [elemsize, ncomp, nelem]
1178795c945Sjeremylt       // vv has shape [nnodes, ncomp]
118e1b98f6eSjeremylt       for (CeedInt e = start*blksize; e < stop*blksize; e+=blksize)
119170d5e71Sjeremylt         for (CeedInt d = 0; d < ncomp; d++)
12006cdd269SJed Brown           for (CeedInt i = 0; i < elemsize*blksize; i+=blksize)
1218d94b059Sjeremylt             // Iteration bound set to discard padding elements
1224ce2993fSjeremylt             for (CeedInt j = i; j < i+CeedIntMin(blksize, nelem-e); j++)
12361dbc9d2Sjeremylt               vv[imode == CEED_NONINTERLACED
1248795c945Sjeremylt                        ? impl->indices[j+e*elemsize]+nnodes*d
12506cdd269SJed Brown                        : d+ncomp*impl->indices[j+e*elemsize]]
126e1b98f6eSjeremylt               += uu[elemsize*(d*blksize+ncomp*e) + j - voffset];
12721617c04Sjeremylt     }
12821617c04Sjeremylt   }
12921617c04Sjeremylt   ierr = CeedVectorRestoreArrayRead(u, &uu); CeedChk(ierr);
13021617c04Sjeremylt   ierr = CeedVectorRestoreArray(v, &vv); CeedChk(ierr);
13121617c04Sjeremylt   if (request != CEED_REQUEST_IMMEDIATE && request != CEED_REQUEST_ORDERED)
13221617c04Sjeremylt     *request = NULL;
13321617c04Sjeremylt   return 0;
13421617c04Sjeremylt }
13521617c04Sjeremylt 
136f10650afSjeremylt //------------------------------------------------------------------------------
137f10650afSjeremylt // ElemRestriction Apply - Common Sizes
138f10650afSjeremylt //------------------------------------------------------------------------------
1391d79ecccSjeremylt static int CeedElemRestrictionApply_Ref_11(CeedElemRestriction r,
1404d2a38eeSjeremylt     CeedInt start, CeedInt stop, CeedTransposeMode tmode,
14161dbc9d2Sjeremylt     CeedInterlaceMode imode, CeedVector u, CeedVector v, CeedRequest *request) {
14261dbc9d2Sjeremylt   return CeedElemRestrictionApply_Ref_Core(r, 1, 1, start, stop, tmode, imode,
1439c36149bSjeremylt          u, v, request);
1444d2a38eeSjeremylt }
1454d2a38eeSjeremylt 
1461d79ecccSjeremylt static int CeedElemRestrictionApply_Ref_18(CeedElemRestriction r,
1474d2a38eeSjeremylt     CeedInt start, CeedInt stop, CeedTransposeMode tmode,
14861dbc9d2Sjeremylt     CeedInterlaceMode imode, CeedVector u, CeedVector v, CeedRequest *request) {
14961dbc9d2Sjeremylt   return CeedElemRestrictionApply_Ref_Core(r, 8, 1, start, stop, tmode, imode,
1509c36149bSjeremylt          u, v, request);
1519c36149bSjeremylt 
1529c36149bSjeremylt }
1539c36149bSjeremylt 
1541d79ecccSjeremylt static int CeedElemRestrictionApply_Ref_31(CeedElemRestriction r,
1559c36149bSjeremylt     CeedInt start, CeedInt stop, CeedTransposeMode tmode,
15661dbc9d2Sjeremylt     CeedInterlaceMode imode, CeedVector u, CeedVector v, CeedRequest *request) {
15761dbc9d2Sjeremylt   return CeedElemRestrictionApply_Ref_Core(r, 1, 3, start, stop, tmode, imode,
1589c36149bSjeremylt          u, v, request);
1599c36149bSjeremylt }
1609c36149bSjeremylt 
1611d79ecccSjeremylt static int CeedElemRestrictionApply_Ref_38(CeedElemRestriction r,
1629c36149bSjeremylt     CeedInt start, CeedInt stop, CeedTransposeMode tmode,
16361dbc9d2Sjeremylt     CeedInterlaceMode imode, CeedVector u, CeedVector v, CeedRequest *request) {
16461dbc9d2Sjeremylt   return CeedElemRestrictionApply_Ref_Core(r, 8, 3, start, stop, tmode, imode,
1659c36149bSjeremylt          u, v, request);
1664d2a38eeSjeremylt }
1674d2a38eeSjeremylt 
168f10650afSjeremylt //------------------------------------------------------------------------------
169f10650afSjeremylt // ElemRestriction Apply
170f10650afSjeremylt //------------------------------------------------------------------------------
171be9261b7Sjeremylt static int CeedElemRestrictionApply_Ref(CeedElemRestriction r,
172074cb416Sjeremylt                                         CeedTransposeMode tmode, CeedVector u,
173be9261b7Sjeremylt                                         CeedVector v, CeedRequest *request) {
174be9261b7Sjeremylt   int ierr;
1759c36149bSjeremylt   CeedInt numblk, ncomp, blksize;
1767509a596Sjeremylt   CeedInterlaceMode imode = CEED_NONINTERLACED;
1774d2a38eeSjeremylt   ierr = CeedElemRestrictionGetNumBlocks(r, &numblk); CeedChk(ierr);
1789c36149bSjeremylt   ierr = CeedElemRestrictionGetNumComponents(r, &ncomp); CeedChk(ierr);
1794d2a38eeSjeremylt   ierr = CeedElemRestrictionGetBlockSize(r, &blksize); CeedChk(ierr);
1807509a596Sjeremylt   CeedElemRestriction_Ref *impl;
1817509a596Sjeremylt   ierr = CeedElemRestrictionGetData(r, (void *)&impl); CeedChk(ierr);
1827509a596Sjeremylt   if (impl->indices)
18361dbc9d2Sjeremylt     ierr = CeedElemRestrictionGetIMode(r, &imode); CeedChk(ierr);
1844d2a38eeSjeremylt 
1859c36149bSjeremylt   CeedInt idx = -1;
1869c36149bSjeremylt   if (blksize < 10)
1879c36149bSjeremylt     idx = 10*ncomp + blksize;
1889c36149bSjeremylt   switch (idx) {
1899c36149bSjeremylt   case 11:
19061dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_11(r, 0, numblk, tmode, imode,
1911d79ecccSjeremylt                                            u, v, request);
1929c36149bSjeremylt     break;
1939c36149bSjeremylt   case 18:
19461dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_18(r, 0, numblk, tmode, imode,
1951d79ecccSjeremylt                                            u, v, request);
1969c36149bSjeremylt     break;
1979c36149bSjeremylt   case 31:
19861dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_31(r, 0, numblk, tmode, imode,
1991d79ecccSjeremylt                                            u, v, request);
2009c36149bSjeremylt     break;
2019c36149bSjeremylt   case 38:
20261dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_38(r, 0, numblk, tmode, imode,
2031d79ecccSjeremylt                                            u, v, request);
2049c36149bSjeremylt     break;
2059c36149bSjeremylt   default:
2064d2a38eeSjeremylt     // LCOV_EXCL_START
2079c36149bSjeremylt     return CeedElemRestrictionApply_Ref_Core(r, blksize, ncomp, 0, numblk,
20861dbc9d2Sjeremylt            tmode, imode, u, v, request);
2094d2a38eeSjeremylt     // LCOV_EXCL_STOP
210be9261b7Sjeremylt   }
2119c36149bSjeremylt }
212be9261b7Sjeremylt 
213f10650afSjeremylt //------------------------------------------------------------------------------
214f10650afSjeremylt // ElemRestriction Apply Block
215f10650afSjeremylt //------------------------------------------------------------------------------
216be9261b7Sjeremylt static int CeedElemRestrictionApplyBlock_Ref(CeedElemRestriction r,
217074cb416Sjeremylt     CeedInt block, CeedTransposeMode tmode, CeedVector u, CeedVector v,
218074cb416Sjeremylt     CeedRequest *request) {
2194d2a38eeSjeremylt   int ierr;
2209c36149bSjeremylt   CeedInt ncomp, blksize;
2217509a596Sjeremylt   CeedInterlaceMode imode = CEED_NONINTERLACED;
2229c36149bSjeremylt   ierr = CeedElemRestrictionGetNumComponents(r, &ncomp); CeedChk(ierr);
2234d2a38eeSjeremylt   ierr = CeedElemRestrictionGetBlockSize(r, &blksize); CeedChk(ierr);
2247509a596Sjeremylt   CeedElemRestriction_Ref *impl;
2257509a596Sjeremylt   ierr = CeedElemRestrictionGetData(r, (void *)&impl); CeedChk(ierr);
2267509a596Sjeremylt   if (impl->indices)
22761dbc9d2Sjeremylt     ierr = CeedElemRestrictionGetIMode(r, &imode); CeedChk(ierr);
2284d2a38eeSjeremylt 
2299c36149bSjeremylt   CeedInt idx = -1;
2309c36149bSjeremylt   if (blksize < 10)
2319c36149bSjeremylt     idx = 10*ncomp + blksize;
2329c36149bSjeremylt   switch (idx) {
2339c36149bSjeremylt   case 11:
23461dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_11(r, block, block+1, tmode, imode,
2354d2a38eeSjeremylt                                            u, v, request);
2369c36149bSjeremylt     break;
2379c36149bSjeremylt   case 18:
23861dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_18(r, block, block+1, tmode, imode,
2394d2a38eeSjeremylt                                            u, v, request);
2409c36149bSjeremylt     break;
2419c36149bSjeremylt   case 31:
24261dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_31(r, block, block+1, tmode, imode,
2439c36149bSjeremylt                                            u, v, request);
2449c36149bSjeremylt     break;
2459c36149bSjeremylt   case 38:
24661dbc9d2Sjeremylt     return CeedElemRestrictionApply_Ref_38(r, block, block+1, tmode, imode,
2479c36149bSjeremylt                                            u, v, request);
2489c36149bSjeremylt     break;
2499c36149bSjeremylt   default:
2504d2a38eeSjeremylt     // LCOV_EXCL_START
2519c36149bSjeremylt     return CeedElemRestrictionApply_Ref_Core(r, blksize, ncomp, block, block+1,
25261dbc9d2Sjeremylt            tmode, imode, u, v, request);
2534d2a38eeSjeremylt     // LCOV_EXCL_STOP
254be9261b7Sjeremylt   }
2559c36149bSjeremylt }
256be9261b7Sjeremylt 
257f10650afSjeremylt //------------------------------------------------------------------------------
258f10650afSjeremylt // ElemRestriction Destroy
259f10650afSjeremylt //------------------------------------------------------------------------------
26021617c04Sjeremylt static int CeedElemRestrictionDestroy_Ref(CeedElemRestriction r) {
26121617c04Sjeremylt   int ierr;
262fe2413ffSjeremylt   CeedElemRestriction_Ref *impl;
263fe2413ffSjeremylt   ierr = CeedElemRestrictionGetData(r, (void *)&impl); CeedChk(ierr);
26421617c04Sjeremylt 
26521617c04Sjeremylt   ierr = CeedFree(&impl->indices_allocated); CeedChk(ierr);
266fe2413ffSjeremylt   ierr = CeedFree(&impl); CeedChk(ierr);
26721617c04Sjeremylt   return 0;
26821617c04Sjeremylt }
26921617c04Sjeremylt 
270f10650afSjeremylt //------------------------------------------------------------------------------
271f10650afSjeremylt // ElemRestriction Create
272f10650afSjeremylt //------------------------------------------------------------------------------
273667bc5fcSjeremylt int CeedElemRestrictionCreate_Ref(CeedMemType mtype, CeedCopyMode cmode,
274667bc5fcSjeremylt                                   const CeedInt *indices, CeedElemRestriction r) {
27521617c04Sjeremylt   int ierr;
27621617c04Sjeremylt   CeedElemRestriction_Ref *impl;
2774ce2993fSjeremylt   CeedInt elemsize, nelem;
2784ce2993fSjeremylt   ierr = CeedElemRestrictionGetNumElements(r, &nelem); CeedChk(ierr);
2794ce2993fSjeremylt   ierr = CeedElemRestrictionGetElementSize(r, &elemsize); CeedChk(ierr);
2804ce2993fSjeremylt   Ceed ceed;
2814ce2993fSjeremylt   ierr = CeedElemRestrictionGetCeed(r, &ceed); CeedChk(ierr);
28221617c04Sjeremylt 
28321617c04Sjeremylt   if (mtype != CEED_MEM_HOST)
284c042f62fSJeremy L Thompson     // LCOV_EXCL_START
2854ce2993fSjeremylt     return CeedError(ceed, 1, "Only MemType = HOST supported");
286c042f62fSJeremy L Thompson   // LCOV_EXCL_STOP
28721617c04Sjeremylt   ierr = CeedCalloc(1,&impl); CeedChk(ierr);
28821617c04Sjeremylt   switch (cmode) {
28921617c04Sjeremylt   case CEED_COPY_VALUES:
2904ce2993fSjeremylt     ierr = CeedMalloc(nelem*elemsize, &impl->indices_allocated);
29121617c04Sjeremylt     CeedChk(ierr);
29221617c04Sjeremylt     memcpy(impl->indices_allocated, indices,
2934ce2993fSjeremylt            nelem * elemsize * sizeof(indices[0]));
29421617c04Sjeremylt     impl->indices = impl->indices_allocated;
29521617c04Sjeremylt     break;
29621617c04Sjeremylt   case CEED_OWN_POINTER:
29721617c04Sjeremylt     impl->indices_allocated = (CeedInt *)indices;
29821617c04Sjeremylt     impl->indices = impl->indices_allocated;
29921617c04Sjeremylt     break;
30021617c04Sjeremylt   case CEED_USE_POINTER:
30121617c04Sjeremylt     impl->indices = indices;
30221617c04Sjeremylt   }
303fe2413ffSjeremylt 
304fe2413ffSjeremylt   ierr = CeedElemRestrictionSetData(r, (void *)&impl); CeedChk(ierr);
305fe2413ffSjeremylt   ierr = CeedSetBackendFunction(ceed, "ElemRestriction", r, "Apply",
306fe2413ffSjeremylt                                 CeedElemRestrictionApply_Ref); CeedChk(ierr);
307be9261b7Sjeremylt   ierr = CeedSetBackendFunction(ceed, "ElemRestriction", r, "ApplyBlock",
308be9261b7Sjeremylt                                 CeedElemRestrictionApplyBlock_Ref);
309be9261b7Sjeremylt   CeedChk(ierr);
310fe2413ffSjeremylt   ierr = CeedSetBackendFunction(ceed, "ElemRestriction", r, "Destroy",
311fe2413ffSjeremylt                                 CeedElemRestrictionDestroy_Ref); CeedChk(ierr);
31221617c04Sjeremylt   return 0;
31321617c04Sjeremylt }
314f10650afSjeremylt //------------------------------------------------------------------------------
315