diff --git a/include/rppt_tensor_audio_augmentations.h b/include/rppt_tensor_audio_augmentations.h
index 8f5b04150..f8ce8899e 100644
--- a/include/rppt_tensor_audio_augmentations.h
+++ b/include/rppt_tensor_audio_augmentations.h
@@ -60,6 +60,23 @@ extern "C" {
*/
RppStatus rppt_non_silent_region_detection_host(RppPtr_t srcPtr, RpptDescPtr srcDescPtr, Rpp32s *srcLengthTensor, Rpp32f *detectedIndexTensor, Rpp32f *detectionLengthTensor, Rpp32f cutOffDB, Rpp32s windowLength, Rpp32f referencePower, Rpp32s resetInterval, rppHandle_t rppHandle);
+/*! \brief To Decibels augmentation on HOST backend
+ * \details To Decibels augmentation for 1D audio buffer converts magnitude values to decibel values
+ * \param[in] srcPtr source tensor in HOST memory
+ * \param[in] srcDescPtr source tensor descriptor (Restrictions - numDims = 3, offsetInBytes >= 0, dataType = F32)
+ * \param[out] dstPtr destination tensor in HOST memory
+ * \param[in] dstDescPtr destination tensor descriptor (Restrictions - numDims = 3, offsetInBytes >= 0, dataType = F32)
+ * \param[in] srcDims source tensor sizes for each element in batch (2D tensor in HOST memory, of size batchSize * 2)
+ * \param[in] cutOffDB minimum or cut-off ratio in dB
+ * \param[in] multiplier factor by which the logarithm is multiplied
+ * \param[in] referenceMagnitude Reference magnitude if not provided maximum value of input used as reference
+ * \param[in] rppHandle RPP HOST handle created with \ref rppCreateWithBatchSize()
+ * \return A \ref RppStatus enumeration.
+ * \retval RPP_SUCCESS Successful completion.
+ * \retval RPP_ERROR* Unsuccessful completion.
+ */
+RppStatus rppt_to_decibels_host(RppPtr_t srcPtr, RpptDescPtr srcDescPtr, RppPtr_t dstPtr, RpptDescPtr dstDescPtr, RpptImagePatchPtr srcDims, Rpp32f cutOffDB, Rpp32f multiplier, Rpp32f referenceMagnitude, rppHandle_t rppHandle);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/modules/cpu/host_tensor_audio_augmentations.hpp b/src/modules/cpu/host_tensor_audio_augmentations.hpp
index dbc9f0f29..76c3193fc 100644
--- a/src/modules/cpu/host_tensor_audio_augmentations.hpp
+++ b/src/modules/cpu/host_tensor_audio_augmentations.hpp
@@ -21,5 +21,6 @@ THE SOFTWARE.
#define HOST_TENSOR_AUDIO_AUGMENTATIONS_HPP
#include "kernel/non_silent_region_detection.hpp"
+#include "kernel/to_decibels.hpp"
#endif // HOST_TENSOR_AUDIO_AUGMENTATIONS_HPP
\ No newline at end of file
diff --git a/src/modules/cpu/kernel/to_decibels.hpp b/src/modules/cpu/kernel/to_decibels.hpp
new file mode 100644
index 000000000..9398da791
--- /dev/null
+++ b/src/modules/cpu/kernel/to_decibels.hpp
@@ -0,0 +1,98 @@
+/*
+Copyright (c) 2019 - 2023 Advanced Micro Devices, Inc. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "rppdefs.h"
+#include
+
+RppStatus to_decibels_host_tensor(Rpp32f *srcPtr,
+ RpptDescPtr srcDescPtr,
+ Rpp32f *dstPtr,
+ RpptDescPtr dstDescPtr,
+ RpptImagePatchPtr srcDims,
+ Rpp32f cutOffDB,
+ Rpp32f multiplier,
+ Rpp32f referenceMagnitude,
+ rpp::Handle& handle)
+{
+ Rpp32u numThreads = handle.GetNumThreads();
+
+ // Calculate the intermediate values needed for DB conversion
+ Rpp32f minRatio = std::pow(10, cutOffDB / multiplier);
+ if(minRatio == 0.0f)
+ minRatio = std::nextafter(0.0f, 1.0f);
+
+ const Rpp32f log10Factor = 0.3010299956639812; //1 / std::log(10);
+ multiplier *= log10Factor;
+
+ omp_set_dynamic(0);
+#pragma omp parallel for num_threads(numThreads)
+ for(int batchCount = 0; batchCount < srcDescPtr->n; batchCount++)
+ {
+ Rpp32f *srcPtrCurrent = srcPtr + batchCount * srcDescPtr->strides.nStride;
+ Rpp32f *dstPtrCurrent = dstPtr + batchCount * dstDescPtr->strides.nStride;
+
+ Rpp32u height = srcDims[batchCount].height;
+ Rpp32u width = srcDims[batchCount].width;
+ Rpp32f refMag = referenceMagnitude;
+
+ // Compute maximum value in the input buffer
+ if(!referenceMagnitude)
+ {
+ refMag = -std::numeric_limits::max();
+ Rpp32f *srcPtrTemp = srcPtrCurrent;
+ if(width == 1)
+ refMag = std::max(refMag, *(std::max_element(srcPtrTemp, srcPtrTemp + height)));
+ else
+ {
+ for(int i = 0; i < height; i++)
+ {
+ refMag = std::max(refMag, *(std::max_element(srcPtrTemp, srcPtrTemp + width)));
+ srcPtrTemp += srcDescPtr->strides.hStride;
+ }
+ }
+ }
+
+ Rpp32f invReferenceMagnitude = (refMag) ? (1.f / refMag) : 1.0f;
+ // Interpret as 1D array
+ if(width == 1)
+ {
+ for(Rpp32s vectorLoopCount = 0; vectorLoopCount < height; vectorLoopCount++)
+ *dstPtrCurrent++ = multiplier * std::log2(std::max(minRatio, (*srcPtrCurrent++) * invReferenceMagnitude));
+ }
+ else
+ {
+ for(int i = 0; i < height; i++)
+ {
+ Rpp32f *srcPtrRow, *dstPtrRow;
+ srcPtrRow = srcPtrCurrent;
+ dstPtrRow = dstPtrCurrent;
+ for(Rpp32s vectorLoopCount = 0; vectorLoopCount < width; vectorLoopCount++)
+ *dstPtrRow++ = multiplier * std::log2(std::max(minRatio, (*srcPtrRow++) * invReferenceMagnitude));
+
+ srcPtrCurrent += srcDescPtr->strides.hStride;
+ dstPtrCurrent += dstDescPtr->strides.hStride;
+ }
+ }
+ }
+
+ return RPP_SUCCESS;
+}
diff --git a/src/modules/rppt_tensor_audio_augmentations.cpp b/src/modules/rppt_tensor_audio_augmentations.cpp
index fbdb75321..adea384b5 100644
--- a/src/modules/rppt_tensor_audio_augmentations.cpp
+++ b/src/modules/rppt_tensor_audio_augmentations.cpp
@@ -60,3 +60,37 @@ RppStatus rppt_non_silent_region_detection_host(RppPtr_t srcPtr,
return RPP_SUCCESS;
}
+
+/******************** to_decibels ********************/
+
+RppStatus rppt_to_decibels_host(RppPtr_t srcPtr,
+ RpptDescPtr srcDescPtr,
+ RppPtr_t dstPtr,
+ RpptDescPtr dstDescPtr,
+ RpptImagePatchPtr srcDims,
+ Rpp32f cutOffDB,
+ Rpp32f multiplier,
+ Rpp32f referenceMagnitude,
+ rppHandle_t rppHandle)
+{
+ if (multiplier == 0)
+ return RPP_ERROR_ZERO_DIVISION;
+ if ((srcDescPtr->dataType == RpptDataType::F32) && (dstDescPtr->dataType == RpptDataType::F32))
+ {
+ to_decibels_host_tensor(static_cast(srcPtr),
+ srcDescPtr,
+ static_cast(dstPtr),
+ dstDescPtr,
+ srcDims,
+ cutOffDB,
+ multiplier,
+ referenceMagnitude,
+ rpp::deref(rppHandle));
+
+ return RPP_SUCCESS;
+ }
+ else
+ {
+ return RPP_ERROR_INVALID_SRC_OR_DST_DATATYPE;
+ }
+}
diff --git a/utilities/test_suite/CMakeLists.txt b/utilities/test_suite/CMakeLists.txt
index 2046c24d3..bfffa6b29 100644
--- a/utilities/test_suite/CMakeLists.txt
+++ b/utilities/test_suite/CMakeLists.txt
@@ -115,4 +115,4 @@ if(Python3_FOUND)
else()
message("-- ${Yellow}Test Warning: Python3 must be installed to run RPP test_suite successfully!${ColourReset}")
-endif(Python3_FOUND)
+endif(Python3_FOUND)
\ No newline at end of file
diff --git a/utilities/test_suite/HIP/Tensor_hip.cpp b/utilities/test_suite/HIP/Tensor_hip.cpp
index cd3fec230..db7df61e0 100644
--- a/utilities/test_suite/HIP/Tensor_hip.cpp
+++ b/utilities/test_suite/HIP/Tensor_hip.cpp
@@ -320,6 +320,18 @@ int main(int argc, char **argv)
double wallTime;
string testCaseName;
+ if(testCase == 82 && imagesMixed)
+ {
+ std::cerr<<"\n RICAP only works with same dimension images";
+ exit(0);
+ }
+
+ if(testCase == 82 && batchSize < 2)
+ {
+ std::cerr<<"\n RICAP only works with BatchSize > 1";
+ exit(0);
+ }
+
// Initialize buffers for any reductionType functions
void *reductionFuncResultArr;
Rpp32u reductionFuncResultArrLength = srcDescPtr->n * 4;
diff --git a/utilities/test_suite/HOST/Tensor_host.cpp b/utilities/test_suite/HOST/Tensor_host.cpp
index 09d1abdd6..4ee99b10c 100644
--- a/utilities/test_suite/HOST/Tensor_host.cpp
+++ b/utilities/test_suite/HOST/Tensor_host.cpp
@@ -318,6 +318,18 @@ int main(int argc, char **argv)
double cpuTime, wallTime;
string testCaseName;
+ if(testCase == 82 && imagesMixed)
+ {
+ std::cerr<<"\n RICAP only works with same dimension images";
+ exit(0);
+ }
+
+ if(testCase == 82 && batchSize < 2)
+ {
+ std::cerr<<"\n RICAP only works with BatchSize > 1";
+ exit(0);
+ }
+
// Initialize buffers for any reductionType functions
void *reductionFuncResultArr;
Rpp32u reductionFuncResultArrLength = srcDescPtr->n * 4;
diff --git a/utilities/test_suite/HOST/Tensor_host_audio.cpp b/utilities/test_suite/HOST/Tensor_host_audio.cpp
index 0ae60d6f9..46a47cdfe 100644
--- a/utilities/test_suite/HOST/Tensor_host_audio.cpp
+++ b/utilities/test_suite/HOST/Tensor_host_audio.cpp
@@ -92,10 +92,6 @@ int main(int argc, char **argv)
noOfAudioFiles = audioNames.size();
}
- // initialize the buffers for audio length and channels
- Rpp32s *srcLengthTensor = (Rpp32s *) calloc(batchSize, sizeof(Rpp32s));
- Rpp32s *channelsTensor = (Rpp32s *) calloc(batchSize, sizeof(Rpp32s));
-
// find max audio dimensions in the input dataset
maxSrcHeight = 1;
maxDstHeight = 1;
@@ -114,10 +110,18 @@ int main(int argc, char **argv)
iBufferSize = (Rpp64u)srcDescPtr->h * (Rpp64u)srcDescPtr->w * (Rpp64u)srcDescPtr->c * (Rpp64u)srcDescPtr->n;
oBufferSize = (Rpp64u)dstDescPtr->h * (Rpp64u)dstDescPtr->w * (Rpp64u)dstDescPtr->c * (Rpp64u)dstDescPtr->n;
- // initialize host buffers for input & output
+ // allocate host buffers for input & output
Rpp32f *inputf32 = (Rpp32f *)calloc(iBufferSize, sizeof(Rpp32f));
Rpp32f *outputf32 = (Rpp32f *)calloc(oBufferSize, sizeof(Rpp32f));
+ // allocate the buffers for audio length and channels
+ Rpp32s *srcLengthTensor = (Rpp32s *) calloc(batchSize, sizeof(Rpp32s));
+ Rpp32s *channelsTensor = (Rpp32s *) calloc(batchSize, sizeof(Rpp32s));
+
+ // allocate the buffers for src/dst dimensions for each element in batch
+ RpptImagePatch *srcDims = (RpptImagePatch *) calloc(batchSize, sizeof(RpptImagePatch));
+ RpptImagePatch *dstDims = (RpptImagePatch *) calloc(batchSize, sizeof(RpptImagePatch));
+
// run case-wise RPP API and measure time
rppHandle_t handle;
rppCreateWithBatchSize(&handle, srcDescPtr->n, 3);
@@ -155,6 +159,24 @@ int main(int argc, char **argv)
break;
}
+ case 1:
+ {
+ testCaseName = "to_decibels";
+ Rpp32f cutOffDB = std::log(1e-20);
+ Rpp32f multiplier = std::log(10);
+ Rpp32f referenceMagnitude = 1.0f;
+
+ for (int i = 0; i < batchSize; i++)
+ {
+ srcDims[i].height = dstDims[i].height = srcLengthTensor[i];
+ srcDims[i].width = dstDims[i].width = 1;
+ }
+
+ startWallTime = omp_get_wtime();
+ rppt_to_decibels_host(inputf32, srcDescPtr, outputf32, dstDescPtr, srcDims, cutOffDB, multiplier, referenceMagnitude, handle);
+
+ break;
+ }
default:
{
missingFuncFlag = 1;
@@ -173,6 +195,29 @@ int main(int argc, char **argv)
maxWallTime = std::max(maxWallTime, wallTime);
minWallTime = std::min(minWallTime, wallTime);
avgWallTime += wallTime;
+
+ // QA mode - verify outputs with golden outputs. Below code doesn’t run for performance tests
+ if (testType == 0)
+ {
+ /* Run only if testCase is not 0
+ For testCase 0 verify_non_silent_region_detection function is used for QA testing */
+ if (testCase != 0)
+ verify_output(outputf32, dstDescPtr, dstDims, testCaseName, dst);
+
+ /* Dump the outputs to csv files for debugging
+ Runs only if
+ 1. DEBUG_MODE is enabled
+ 2. Current iteration is 1st iteration
+ 3. Test case is not 0 */
+ if (DEBUG_MODE && iterCount == 0 && testCase != 0)
+ {
+ std::ofstream refFile;
+ refFile.open(func + ".csv");
+ for (int i = 0; i < oBufferSize; i++)
+ refFile << *(outputf32 + i) << "\n";
+ refFile.close();
+ }
+ }
}
}
rppDestroyHost(handle);
@@ -193,6 +238,8 @@ int main(int argc, char **argv)
// free memory
free(srcLengthTensor);
free(channelsTensor);
+ free(srcDims);
+ free(dstDims);
free(inputf32);
free(outputf32);
return 0;
diff --git a/utilities/test_suite/HOST/runAudioTests.py b/utilities/test_suite/HOST/runAudioTests.py
index ecfb2c23d..cb78ae000 100644
--- a/utilities/test_suite/HOST/runAudioTests.py
+++ b/utilities/test_suite/HOST/runAudioTests.py
@@ -31,7 +31,7 @@
scriptPath = os.path.dirname(os.path.realpath(__file__))
inFilePath = scriptPath + "/../TEST_AUDIO_FILES/three_samples_single_channel_src1"
caseMin = 0
-caseMax = 0
+caseMax = 1
# Checks if the folder path is empty, or is it a root folder, or if it exists, and remove its contents
def validate_and_remove_files(path):
@@ -121,13 +121,13 @@ def run_performance_test(loggingFolder, srcPath, case, numRuns, testType, batchS
def rpp_test_suite_parser_and_validator():
parser = argparse.ArgumentParser()
parser.add_argument("--input_path", type = str, default = inFilePath, help = "Path to the input folder")
- parser.add_argument("--case_start", type = int, default = caseMin, help = "Testing range starting case # - (0:0)")
- parser.add_argument("--case_end", type = int, default = caseMax, help = "Testing range ending case # - (0:0)")
+ parser.add_argument("--case_start", type = int, default = caseMin, help = "Testing start case # - Range must be in [" + str(caseMin) + ":" + str(caseMax) + "]")
+ parser.add_argument("--case_end", type = int, default = caseMax, help = "Testing end case # - Range must be in [" + str(caseMin) + ":" + str(caseMax) + "]")
parser.add_argument('--test_type', type = int, default = 0, help = "Type of Test - (0 = QA tests / 1 = Performance tests)")
parser.add_argument('--qa_mode', type = int, default = 0, help = "Run with qa_mode? Output audio data from tests will be compared with golden outputs - (0 / 1)", required = False)
parser.add_argument('--case_list', nargs = "+", help = "List of case numbers to test", required = False)
parser.add_argument('--num_runs', type = int, default = 1, help = "Specifies the number of runs for running the performance tests")
- parser.add_argument('--preserve_output', type = int, default = 1, help = "preserves the output of the program - (0 = override output / 1 = preserve output )" )
+ parser.add_argument('--preserve_output', type = int, default = 1, help = "preserves the output of the program - (0 = override output / 1 = preserve output )")
parser.add_argument('--batch_size', type = int, default = 1, help = "Specifies the batch size to use for running tests. Default is 1.")
args = parser.parse_args()
@@ -234,7 +234,7 @@ def rpp_test_suite_parser_and_validator():
run_performance_test(loggingFolder, srcPath, case, numRuns, testType, batchSize, outFilePath)
# print the results of qa tests
-supportedCaseList = ['0']
+supportedCaseList = ['0', '1']
nonQACaseList = [] # Add cases present in supportedCaseList, but without QA support
if testType == 0:
@@ -259,7 +259,7 @@ def rpp_test_suite_parser_and_validator():
resultsInfo += "\n - Total augmentations with golden output QA test support = " + str(len(supportedCaseList) - len(nonQACaseList))
resultsInfo += "\n - Total augmentations without golden ouput QA test support (due to randomization involved) = " + str(len(nonQACaseList))
f.write(resultsInfo)
- print("\n-------------------------------------------------------------------" + resultsInfo + "\n\n-------------------------------------------------------------------")
+ print("\n-------------------------------------------------------------------" + resultsInfo + "\n\n-------------------------------------------------------------------")
# Performance tests
if (testType == 1):
diff --git a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_237-126133-0020.txt b/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_237-126133-0020.txt
deleted file mode 100644
index 1d3d8c5da..000000000
--- a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_237-126133-0020.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-0
-35840
diff --git a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_2830-3979-0005.txt b/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_2830-3979-0005.txt
deleted file mode 100644
index d8aecd9b7..000000000
--- a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_2830-3979-0005.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-0
-33680
diff --git a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_6829-68769-0045.txt b/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_6829-68769-0045.txt
deleted file mode 100644
index 28d416ab6..000000000
--- a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/non_silent_region_detection/non_silent_region_detection_ref_6829-68769-0045.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-0
-34160
diff --git a/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/to_decibels/to_decibels.bin b/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/to_decibels/to_decibels.bin
new file mode 100644
index 000000000..9ab2f32dc
--- /dev/null
+++ b/utilities/test_suite/REFERENCE_OUTPUTS_AUDIO/to_decibels/to_decibels.bin
@@ -0,0 +1,232 @@
+4848484848_j3I@:484848484848
148´,484848Bޡ48484848XEG4848484888ޡ48484848_8Z.Z484848
ZB}484848ZLGj48484848.ZB4848}"3ޡz_ٝj4848\zR4848484848`J)j4848484848C)z4848484848sv4848}4848´,484848486ͽ^48484848
4848µ48484848
1r4848484848NZ484848148484848484848484848484848
8}8484848³1`4848o>4848"31|
s484848484848483I
4848484848 48N4848483I4848
N48484848@BU48481484848484848 Z!L484848N4848
BsN4848148484811484848484848@ZB,
484848`
#,Z!@ޡ}484848
`C4848484848
848ºK!84848 4848ZJ)48484848
!8@o>
48484848C!8 484848"3)R:148484848
B84848484848.Z}jޡ48484848zj: 484848rEY2,8484848
N48484848 XE4848484848N484848484848,>
P48484848´,YY484848484848µ
#,B48484848484848P484848484848484848"3484848Z&48483I14848ޡ@484848484848j,4848ٝ484848 8484848484848484848
84848B)484848OY48484848PZ&48484848484848)̬_!48484848484848_!6ͽ484848R484848483Iȷ48484848
84848}8484848484848\089<:48484848@`XjMj484848`X484884848484848484848>^+484848
, Z&48"34848J)>y:j484848µ@1Z484848zo> 48484848^|6ͽ|ٝ484848)@BU|48484848148484848484848¶*ΧK4848484848488J)`48484848º̂
48483I484848J)48484848\L484848µj48484848484848¶Q48484848484848P8|48484848ºo>48484848
8s484848CR4848484848484848ٝZ&48484848_!ή484848
_!48484848j484848v {484848;fLo>48484848
j)3I48484848/v4848Go4848484848B!8ޡ4848"348!8G_!z48484848
&z484848
8Z4848v8r8484848484848o>j48¿ j3I848
84848}jjN484848cP4848ʦ`484848.Z:1@R4848484848484848)484848¶z4848sʦZs`j48484848
48481!8}4848Z&v.484848,zC"34848484848r4848484848@
4848484848C4848\OΤ>484848N48484848µ4848}84848K484848s484848º484848484848484848j.ٝ8Z&ޡ484884848N484848)484848j484848ƨ484848`}jR488XE484848¶Z!
zZ!4848484848L484848r̂4848R/vz4848j#,
48`14848488 )
8484848
4848o>48484848 s)484848
j148XE'4848Bj48 484848&ZdzBSC48484848BS#,4848
v;f16ͽ48484848c3R
484848C`,48£J)4848BU\484848o`4848Bv"34848483I,4848o>)48481z484848848}BS4848484848BYN48)48484848Qr 48484848C@+o 484848484848sBZ484848
8 84848484848,48º4848484848^K48484848483I4848o>48484848z484848484848Nv`48 4848483RJ)48¶48484848j48484848484848µz48484848¡_4848#,\484848484848`KG4848 {48484848s4848484848C4848`X48
z484848Z!o>48P.y1s48#,~484848M1RC4848,4848'084848@MBS48¡1484848Pz48
|48484848484848484848r484848NSA9&_!484848s4848¿ Zdz484848B484848W٠.Z4848¿ 484848`G484848,\sN48484848B4848"3,j48µ`484848Q4848NZ!@4848@N:@4848G4848481N4848Z484848ZdzC48"3484848B'48484848GXE48 484848`X@P48484848
j48484848\1R+484848484848
48484848@ C48488Z&jo@484848K7~484848s_"3^|4848484848B48´,o>#,Z3I_484848#,S4848
,484848BS;f484848º:484848 LBU B4848484848CNrK48484848r484848484848
4848P"3}P48ٝ4848>Z!48484848484848~ 484848484848~"34848
4848¶̬48484848N3I4848484848J)ro4848BU484848o4848 4848+/v4848j*Χ {48484848484848_!`) 4848Z&R@4848484848483I_484848
B484848ʦZ!3I4848
4848484848̂'48484848484848¿:j148Z&1,48484848.Z48484848.Zr4848484848
O/v48484848481rG484848
48ޡ뽯z484848`Z4848484848484848Cs484848)j}j}484848BSK48Z&ή48484848jZZ48484848N/vz48}Z!484848ޡ48ޡz4848J)4848|_!ޡ48484848ʦ4848
Bo>4848)4848#,Z!B4848483Iٝ,Z뽯14848¡)4848jBS`48483I^|484848P_48484848484848zG484848@N48ٝL484848vȷ}j}48484848483I~4848¿ P48
N484848§)XEs484848484848`)484848 484848³1ޡ4848"3XE4848"3^|484848}484848+K484848484848488484848483I4848¿:Rv4848@484848yG484848,|@`484848483I_!4848484848Z&K3I48484848NJ),ٝ48484848}jBU48P}j:,48_484848
14848484848481N@ `"348483I^|s1_!KZ!4848s\!8484848¡484848º4814848@o>}4848£ZdzP4848481>tP484848484848µXEN484848³1BS4848484814848´,ٝ 4848 4848\ٝ
"348484848N_4848µ
484848§)ȷ4848484848 N,484848484848BU.Z4848484848484848
J)4848484848484848Rzz4848484848|o484848ޡ48484848484848484848N\s484848´,4848Bޡ48484848484848XE484848Z~GZ&48484848N\484848484848N.484848¶ 84848z1484848XE@"3484848,s484848}R48484848484848}481o>r
484848.ZK#,}484848!8j4848484848µ#,K|4848@ٝs4848484848Z1L4848484848o>Z!Y
484848
J)Kj4848Z&Bޡz\No>848}R48484848s`484848484848484848PQ14848j_!B4848R"348484848@C48484848148Z&J)484848o>
ޡ48484848484848,484848484848_!1,48484848
8o>
484848484848B |484848484848
48484848484848484848Z& 484848j@484848:48Z&C4848
8
484848_s484848484848¿ @4848484848
N@4848s4848ٝ
484848`3I48484848_`484848484818}`4848o>y ^|)`4848`Z&_!48483Iz4848484848Z& |48C48484848148´,484848`js4848_:_484848484848@y3IB4848"3484848484848C8_C48484848J)Z!BUR48484848
488o> 48484848`J)1484848484848µ 8484848ޡ@48"3_
4848484848#,C484848484848488484848ٝ}
484848Z&r @484848484848484848484848ޡ`48484848484848No>4848484818481,B484848`P484848
8"3484848Z&4848 ,
48484848484848484848´,s"3484848_!484848484848z484848`84848`48484848ޡsC484848N!8jP48484848,y48484848£!8L1484848@
4848Z&_N}js48484848µ@_
484848}_ޡ
R
4848`848484848 "3P3I_Z&484848
s
,484848484848"34848`484848484848¶1484848484848s1o>484848484848@8
48484848"3B4848484848"3}484848484848
R48484848484848
14848484848484814848
4848484848484848484848"34848484848484848µ4848484848484848Cj48484848484848484848481`4848
,484848
88Z&4848484848
@48C 484848C
4848Z&CPP 4848484848"3sB8``48´, 48´,48´,1488y8484848"3zXE@s4848´,
3IN3I8}@}8 P84848`C4848P}ޡ484848484848}4848Cޡs3I@48484848"3`}@,4848484848484848µ@8`,Z&14848484848484848P3I8484848
848484848484848484848
Z&48484848}48484848484848484848484848
8484848CB48484848
Z&4848484848484848 14848488484848484848´,,4848484848484848´,484848484848481C48484848484848484848C`C,484848484848B
4848484848
}48"3ޡ4848µ`BZ&CZ&484848o>N "3 _s1@484848Z&"38
"31B}o>o>ޡ}884848
848´, 8}
@ޡ3I3I484848484848Ro>}
484848484848
"3 ޡ`4848´,Z&`
48BPZ&48484848484848
,,484848484848
48484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848
848484848484848
Z&8C484848484848484848
48µ 484848484848484848484848484848Z&
,CBZ&C`48484848Z&48µ48484848C
4848484848µ4848`4848C484848484848"33I48Z&4848µ4848484848484848 ,
4848Z&48C48C4848484848484848484848484848484848484848
48CZ&48484848484848484848484848CZ&Z&,"348484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848484848
48484848´,4848
8C,8 ,484848Z&4848484848484848C48484848
4848B8"3Z&81CZ&
48`"348
848C8 B
}}"3"3"3}1@`1 }@`ޡRB81@3I3I1
_s3I1ޡ@3I3I@ޡP8P8@@@8`"3 "3
14818`48,48483I1 48µ14848`48
4848´,8
48"3
,8
84848 ,
8B1ޡ8ޡB
"3Z&,ޡ1Z&,8,8484848 48`481Z&B1"3Z& 48@@13Iޡ1 ޡ
_8sP}@@@`88_1@8R8
J)Ns} NZ o>ZR#,,o>:#,
Z
@).ZٝZXENNz :@BUZy: N Kr,}XEy!8^|\ʦ|:#,N@1|Njʦ {BSz|NJ) y+ {o@@8z
\zٝZ&rKb4h)BUs4848484848#,)BZ!zP}Z&B, { {.Z}zN\BUz484848z484848ޡNo@#,J)@48"3N 48484848
}jNj\ή oޡ4848484848CsBUP48484848484848\hvvrN484848ޡrߴ)48484848N+ox\Z}484848484848\PB484848z {4848~@Z484848484848Z*Z484848488ٝ {N3Rٝj4848484848"3&8-եύY248484848Z&48´,484848.Y4848̬.48484848Ӄ.Z48}jhs484848481"34848rϣ 6ͽL48484848v#5L484848³1484848_ 'LN484848484848+4848
By4848NP48484848Bȷ484848Y2484848j484848N}jB4848+ol-4848484848484848484848Nd1484848.ZSAW48484848481)+_48
8 :rߴ4848484848484848ޡ484848R48484848484848>W[\@48484848¶.BZY2+o484848 4848484848 Y2N484848489&k/-եo>484848´,#,4848484848|48AjXۘv4848484848§)4848484848B*@Z&_"34848484848484848ŝ
Boo6ͽ^4848484848v_!Z&48484848ޡBgb 48484848}
`)MK4848487G뽯48484848489&)\8U48484848.Z)484848484848Rv[
+o4848484848484848R484848rEsc48484848¿ 4848
ko4848s4848484848@484848489<U%0848484848484848484848484848zٝrߴ/v|48484848}48484848ή䇢r484848484848ޡz 4848484848484848484848b4dϑ3S~@484848484848484848§B>Eu|4848484848484848 ͜ ύ#,48484848ْ͍
G4848484848484848
矑~6ͽ4848484848484848Z&o>Z&ޡ48484848484848 1P48484848´,4848484848484848Q'484848w4848¿ `484848483IrOBS48484848 .k
48484848484848^䇢48Zȷ4848K484848N8|͍ȷ)4848484848484848484848K 48484848484848.ϣ.,4887_(,484848z484848@_W4848³148484848\͍Z&4848484848eL3I4848}o8.h48484848¿ /vȷ484848484848r|ޡ484848484848zY248484883I@Y%h084848ue*l3\}C484848484848!8QjC484848484848484848484848484848484848z1[7zH9xɉA/v84848484848L484848484848484848484848484848484848486r|1&4p.iMrق!ܴC҇2I契(Z&4848484848484848484848ޡ484848484848484848Z&"ۘOΤmPvd)S}V[
YSLORd_*Z&484848484848484848ƨ 484848484848484848N5/KIGi8e]2S__VaVI]Z?G>tBqSNz484848484848484848w}o2y48484848484848484848\*Y|Mr5m8hMmJ,ky[KA95sa4848484848484848
Btso4848484848484848484848«c|jY~]fbpe
px(xHR5'
+%-"RFM484848484848480$vSqnC _!484848484848484848 OőRTanneqwz{j K
+0 rjC&_B148484848484848¾҇*WTUKnwMwʦ484848484848484848/ΝW?uKM\OVWc#Wa6=h)62$!B,484848484848BgG7MwXERGr4848484848484848HtC>1C$ITRd}X 5Q"3`"7)8k48484848484848SA_rrSu2_Ք48484848484848484848¡]f@qDLr%Q
Sk`s)`;8%h0m+DM.X{)HR48484848484848]zKm`L48zՄ"q48484848484848olS;7;?ӑDP9e_]2>[.(ыÀ1C48484848484848QTP
+4848¥.mTn48484848484848KfD vT[!B5&eN3 Hf4848484848484848,{q$oe4848Ҍ͒nsN48484848484848 NEoG;F~@Y>FFz2KI,>K=0neq48484848484848480me
pÀxQ>lZEb.Y48484848484848\}bNH=QD];q583<&1Ʊ?~"zB484848484848484848Vle488hEH?q4848484848484848SkoJLJN5<+B1z26[^+Fh z!3M484848484848484848G.48DT=.?b\v48484848484848z]