diff --git a/root-Use-different-output-filenames-in-tests-pdftitle.cxx.patch b/root-Use-different-output-filenames-in-tests-pdftitle.cxx.patch deleted file mode 100644 index ac337b4..0000000 --- a/root-Use-different-output-filenames-in-tests-pdftitle.cxx.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 28cff2a71e084210b9265cc1c6a4691afc840556 Mon Sep 17 00:00:00 2001 -From: Mattias Ellert -Date: Sat, 27 Jun 2026 15:56:57 +0200 -Subject: [PATCH] Use different output filenames in tests pdftitle.cxx and - pdfurl.cxx - -This avoids failures when one test overwrites the other's output: - -[==========] Running 1 test from 1 test suite. -[----------] Global test environment set-up. -[----------] 1 test from TPad -[ RUN ] TPad.PDFUrl -ROOT::TestSupport::ForbidDiagnostics::handler(): Diagnostic in 'TCanvas::Print': -pdf file output.pdf has been created -ROOT::TestSupport::ForbidDiagnostics::handler(): Diagnostic in 'TCanvas::Print': -Current canvas added to pdf file output.pdf -ROOT::TestSupport::ForbidDiagnostics::handler(): Diagnostic in 'TCanvas::Print': -Current canvas added to pdf file output.pdf -ROOT::TestSupport::ForbidDiagnostics::handler(): Diagnostic in 'TCanvas::Print': -pdf file output.pdf has been closed -/builddir/build/BUILD/root-6.40.02/graf2d/gpad/test/pdfurl.cxx:33: Failure -Expected equality of these values: - statCode - Which is: 1 - 0 -PDF file was not created. -[ FAILED ] TPad.PDFUrl (373 ms) -[----------] 1 test from TPad (373 ms total) -[----------] Global test environment tear-down -[==========] 1 test from 1 test suite ran. (374 ms total) -[ PASSED ] 0 tests. -[ FAILED ] 1 test, listed below: -[ FAILED ] TPad.PDFUrl - 1 FAILED TEST ---- - graf2d/gpad/test/pdftitle.cxx | 2 +- - graf2d/gpad/test/pdfurl.cxx | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/graf2d/gpad/test/pdftitle.cxx b/graf2d/gpad/test/pdftitle.cxx -index 9162df2cd84..57c0935133f 100644 ---- a/graf2d/gpad/test/pdftitle.cxx -+++ b/graf2d/gpad/test/pdftitle.cxx -@@ -5,7 +5,7 @@ - - TEST(TPad, PDFTitle) - { -- const TString pdfFile = "output.pdf"; -+ const TString pdfFile = "pdftitle.pdf"; - - // Generate a multi-page PDF with a title - TCanvas c; -diff --git a/graf2d/gpad/test/pdfurl.cxx b/graf2d/gpad/test/pdfurl.cxx -index c0a77ce8dc9..43b114c8f41 100644 ---- a/graf2d/gpad/test/pdfurl.cxx -+++ b/graf2d/gpad/test/pdfurl.cxx -@@ -6,7 +6,7 @@ - - TEST(TPad, PDFUrl) - { -- const TString pdfFile = "output.pdf"; -+ const TString pdfFile = "pdfurl.pdf"; - - // Generate a multi-page PDF with page titles and #url in TLatex - TCanvas c1; --- -2.54.0 - diff --git a/root-no-export-python-modules.patch b/root-no-export-python-modules.patch index f5c5106..d3522f5 100644 --- a/root-no-export-python-modules.patch +++ b/root-no-export-python-modules.patch @@ -1,4 +1,4 @@ -From 4b14d9d83ff154eca3672570f9be66513268209f Mon Sep 17 00:00:00 2001 +From ea7bed3a096949588b1b9086ddf66b2f7892a385 Mon Sep 17 00:00:00 2001 From: Mattias Ellert Date: Thu, 4 Dec 2025 23:13:28 +0100 Subject: [PATCH] Dont export Python modules @@ -9,18 +9,18 @@ Subject: [PATCH] Dont export Python modules 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt b/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt -index c21fb317f8c..7f1717e3f4b 100644 +index 95ca00bde9f..a12d3f05e4d 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt +++ b/bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt -@@ -117,8 +117,7 @@ install(TARGETS CPyCppyy EXPORT ${CMAKE_PROJECT_NAME}Exports +@@ -120,8 +120,7 @@ install(TARGETS CPyCppyy EXPORT ${CMAKE_PROJECT_NAME}Exports LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries) -install(TARGETS cppyy EXPORT ${CMAKE_PROJECT_NAME}Exports -- RUNTIME DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/cppyy COMPONENT libraries # Windows -+install(TARGETS cppyy RUNTIME DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/cppyy COMPONENT libraries # Windows - LIBRARY DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/cppyy COMPONENT libraries - ARCHIVE DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/cppyy COMPONENT libraries) +- RUNTIME DESTINATION ${CPPYY_INSTALL_DIR} COMPONENT libraries # Windows ++install(TARGETS cppyy RUNTIME DESTINATION ${CPPYY_INSTALL_DIR} COMPONENT libraries # Windows + LIBRARY DESTINATION ${CPPYY_INSTALL_DIR} COMPONENT libraries + ARCHIVE DESTINATION ${CPPYY_INSTALL_DIR} COMPONENT libraries) diff --git a/bindings/pyroot/pythonizations/CMakeLists.txt b/bindings/pyroot/pythonizations/CMakeLists.txt index 32d7d54b8da..5128e6d337c 100644 @@ -50,5 +50,5 @@ index 32d7d54b8da..5128e6d337c 100644 ARCHIVE DESTINATION ${pymoduledir_install} COMPONENT libraries) -- -2.54.0 +2.55.0 diff --git a/root-pdf-Fix-calculation-of-kerning-distances-in-generate.patch b/root-pdf-Fix-calculation-of-kerning-distances-in-generate.patch new file mode 100644 index 0000000..598d7b0 --- /dev/null +++ b/root-pdf-Fix-calculation-of-kerning-distances-in-generate.patch @@ -0,0 +1,68 @@ +From 87e5571eef573a1ab802c9b5b68e4215045689b9 Mon Sep 17 00:00:00 2001 +From: Mattias Ellert +Date: Sat, 29 Aug 2026 13:15:53 +0200 +Subject: [PATCH] [pdf] Fix calculation of kerning distances in generated PDF + files + +Note that this uses the system fonts on Fedora rather than the bundled +fonts in the root sources. + +Using the following test program + +void ttest() +{ + auto c = new TCanvas("c", "", 300, 100); + auto t = new TText(0.1, 0.4, "Raw assymmetry"); + t->SetTextSize(0.5); + t->SetTextFont(132); + t->Draw(); + c->Print("ttest.pdf"); + c->Print("ttest.eps"); + c->Print("ttest.png"); +} + +The .eps and .png images look like expected. But the .pdf image only +shows the initial capital "R", not the full "Raw assymmetry" text. + +After decompressing the generated .pdf file: + +cpdf -decompress ttest.pdf -o ttest.dec.pdf + +the decompressed file shows that the kerning distances are incorrect: + +[(R) -2147483648(a) 24(w) -2147483648( ) 0(a) 0(s) -2147483648(s) 17(y) 0(m) 0(m) 8(e) 8(t) 0(r) -2147483648(y) ] + +Something has clearly gone wrong here. -2147483648 is the minimum +value that can be stored in a 16 bit signed integer. + +The origin of the problem is a calculation of a difference between a +UInt_t (i.e. unsigned) value and an Int_t (i.e. signed) value in the +TPDF::Text() method. Casting the UInt_t to an Int_t before calculating +the difference, as done in this commit, fixes the problem. With this +change the kerning distances are better: + +[(R) -2(a) 24(w) -2( ) 0(a) 0(s) -2(s) 17(y) 0(m) 0(m) 8(e) 8(t) 0(r) -2(y) ] + +and the generated .pdf file shows the complete text. The generated +.png and .eps files do not change (except for the time stamp in the +.eps file). +--- + graf2d/postscript/src/TPDF.cxx | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/graf2d/postscript/src/TPDF.cxx b/graf2d/postscript/src/TPDF.cxx +index 21a2506010f..e1cd9b10d10 100644 +--- a/graf2d/postscript/src/TPDF.cxx ++++ b/graf2d/postscript/src/TPDF.cxx +@@ -1943,7 +1943,7 @@ void TPDF::Text(Double_t xx, Double_t yy, const char *chars) + tmp[0] = chars[i-1]; + UInt_t width=0; + t.GetTextAdvance(width, &tmp[0], kFALSE); +- Double_t wwl = gPad->AbsPixeltoX(width - charDeltas[i]) - gPad->AbsPixeltoX(0); ++ Double_t wwl = gPad->AbsPixeltoX((Int_t)width - charDeltas[i]) - gPad->AbsPixeltoX(0); + wwl -= 0.5*(gPad->AbsPixeltoX(1) - gPad->AbsPixeltoX(0)); // half a pixel ~ rounding error + charDeltas[i] = (Int_t)((1000.0/Float_t(fontsize))*(XtoPDF(wwl) - XtoPDF(0))/scale); + } +-- +2.55.0 + diff --git a/root-tmva-sofie-Complete-big-endian-support-for-ONNX-init.patch b/root-tmva-sofie-Complete-big-endian-support-for-ONNX-init.patch new file mode 100644 index 0000000..d7d0181 --- /dev/null +++ b/root-tmva-sofie-Complete-big-endian-support-for-ONNX-init.patch @@ -0,0 +1,146 @@ +From 2fec5822e305b9b203894aa0fe3e4dded1f673cc Mon Sep 17 00:00:00 2001 +From: Jonas Rembser +Date: Wed, 8 Jul 2026 11:13:16 +0200 +Subject: [PATCH] [tmva][sofie] Complete big-endian support for ONNX + initializer tensors +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Follow-up to the last commit ("[tmva][sofie] Fix big endian"), which +byte-swapped raw_data initializers on big-endian platforms but left a +few gaps: + +- Initializers with data_location == EXTERNAL were still read from the + external weights file without swapping, silently producing + byte-reversed weights on big-endian machines. + +- The type switch only handled FLOAT, DOUBLE, INT32 and INT64. One-byte + types (BOOL, UINT8, INT8), which parse fine on little-endian via + plain memcpy, now threw on big-endian, and the other fixed-width + types (INT16, UINT16, UINT32, UINT64) were rejected as well. + +- The swap loops dereferenced the protobuf string buffer through + reinterpret_cast'ed uint32_t*/uint64_t* pointers, which is a + misaligned access on strict-alignment targets (e.g. SPARC) since + the buffer carries no alignment guarantee. + +Replace the four copy-pasted swap loops with a width-dispatched helper, +CopyLEToHost, that plain-copies 1-byte element types and byte-swaps +2/4/8-byte elements through a memcpy'd temporary (no misaligned loads, +valid for in-place use), and call it from both the raw_data and the +external-data paths. + +🤖 Done with the help of AI +--- + tmva/sofie_parsers/src/RModelParser_ONNX.cxx | 73 ++++++++++++-------- + 1 file changed, 45 insertions(+), 28 deletions(-) + +diff --git a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx +index ac4cc7d5740..1c14d12ba30 100644 +--- a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx ++++ b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx +@@ -4,6 +4,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -155,6 +156,45 @@ struct ExtractDataFromTP { + } + }; + ++#ifndef R__BYTESWAP ++namespace { ++ ++// Copy nbytes from source to dest, byte-swapping each N-byte element. The ++// temporary avoids misaligned loads from the protobuf string buffer and makes ++// in-place swapping (dest == source) valid. ++template ++void CopyBswap(void *dest, const void *source, std::size_t nbytes) ++{ ++ using value_type = typename RByteSwap::value_type; ++ auto dst = static_cast(dest); ++ auto src = static_cast(source); ++ for (std::size_t k = 0; k < nbytes; k += N) { ++ value_type v; ++ std::memcpy(&v, src + k, N); ++ v = RByteSwap::bswap(v); ++ std::memcpy(dst + k, &v, N); ++ } ++} ++ ++// Copy a buffer of little-endian tensor elements to host (big-endian) byte order ++void CopyLEToHost(void *dest, const void *source, std::size_t nbytes, ETensorType tensor_type) ++{ ++ switch (GetTypeSize(tensor_type)) { ++ case 1: ++ if (dest != source) ++ std::memcpy(dest, source, nbytes); ++ break; ++ case 2: CopyBswap<2>(dest, source, nbytes); break; ++ case 4: CopyBswap<4>(dest, source, nbytes); break; ++ case 8: CopyBswap<8>(dest, source, nbytes); break; ++ default: ++ throw std::runtime_error("Data type " + ConvertTypeToString(tensor_type) + " in tensor is not supported!\n"); ++ } ++} ++ ++} // anonymous namespace ++#endif ++ + std::shared_ptr RModelParser_ONNX::GetInitializedTensorData(onnx::TensorProto *tensorproto, size_t tensor_size, ETensorType tensor_type) + { + +@@ -172,34 +212,7 @@ std::shared_ptr RModelParser_ONNX::GetInitializedTensorData(onnx::TensorPr + std::memcpy(data.get(), tensorproto->raw_data().c_str(), tensor_size); + #else + // big-endian architectures - need to swap bytes +- switch (tensor_type) { +- case ETensorType::FLOAT: +- for (std::size_t k = 0; k < tensor_size / sizeof(float); ++k) +- (reinterpret_cast::value_type *>(data.get()))[k] = +- RByteSwap::bswap((reinterpret_cast::value_type *>( +- tensorproto->raw_data().c_str()))[k]); +- break; +- case ETensorType::DOUBLE: +- for (std::size_t k = 0; k < tensor_size / sizeof(double); ++k) +- (reinterpret_cast::value_type *>(data.get()))[k] = +- RByteSwap::bswap((reinterpret_cast::value_type *>( +- tensorproto->raw_data().c_str()))[k]); +- break; +- case ETensorType::INT32: +- for (std::size_t k = 0; k < tensor_size / sizeof(int32_t); ++k) +- (reinterpret_cast::value_type *>(data.get()))[k] = +- RByteSwap::bswap((reinterpret_cast::value_type *>( +- tensorproto->raw_data().c_str()))[k]); +- break; +- case ETensorType::INT64: +- for (std::size_t k = 0; k < tensor_size / sizeof(int64_t); ++k) +- (reinterpret_cast::value_type *>(data.get()))[k] = +- RByteSwap::bswap((reinterpret_cast::value_type *>( +- tensorproto->raw_data().c_str()))[k]); +- break; +- default: +- throw std::runtime_error("Data type " + ConvertTypeToString(tensor_type) + " in tensor is not supported!\n"); +- } ++ CopyLEToHost(data.get(), tensorproto->raw_data().c_str(), tensor_size, tensor_type); + #endif + } else { + // case tensor data are stored as specific types and not in raw_data +@@ -262,6 +275,10 @@ std::shared_ptr RModelParser_ONNX::GetInitializedTensorData(onnx::TensorPr + + fDataFile.seekg(offset); + fDataFile.read(reinterpret_cast(data.get()), buffer_size); ++#ifndef R__BYTESWAP ++ // external data is stored little-endian like raw_data - swap in place ++ CopyLEToHost(data.get(), data.get(), buffer_size, tensor_type); ++#endif + } + + return data; +-- +2.55.0 + diff --git a/root.spec b/root.spec index e316729..0b03a05 100644 --- a/root.spec +++ b/root.spec @@ -38,9 +38,9 @@ %global __provides_exclude_from ^%{python3_sitearch}/.*/lib.*\\.so$ Name: root -Version: 6.40.02 +Version: 6.40.04 %global libversion %(cut -d. -f 1-2 <<< %{version}) -Release: 5%{?dist} +Release: 1%{?dist} Summary: Numerical data analysis framework License: LGPL-2.1-or-later @@ -87,14 +87,15 @@ Patch6: %{name}-Save-memory-Do-not-link-to-LLVM-libraries-in-parallel.patch Patch7: %{name}-Revert-cppyy-Mark-addressof-test-as-xfail-on-modules.patch # Adjust stressGraphics reference Patch8: %{name}-Adjust-stressGraphics-reference.patch -# https://github.com/root-project/root/pull/22722 -Patch9: %{name}-Use-different-output-filenames-in-tests-pdftitle.cxx.patch # https://github.com/root-project/root/pull/22723 -Patch10: %{name}-tmva-sofie-Fix-big-endian.patch +Patch9: %{name}-tmva-sofie-Fix-big-endian.patch +Patch10: %{name}-tmva-sofie-Complete-big-endian-support-for-ONNX-init.patch # https://github.com/root-project/root/pull/22724 Patch11: %{name}-core-The-old-TUUID-constructor-can-create-either-ver.patch Patch12: %{name}-tree-df-Do-not-fail-test-on-32-bit-due-to-mis-aligne.patch Patch13: %{name}-tree-nt-Compare-size-to-the-size-of-the-struct.patch +# https://github.com/root-project/root/pull/23188 +Patch14: %{name}-pdf-Fix-calculation-of-kerning-distances-in-generate.patch BuildRequires: gcc-c++ BuildRequires: gcc-gfortran @@ -426,7 +427,7 @@ Obsoletes: %{name}-notebook < 6.32.00 # Package renamed (jupyroot is now a submodule) %py_provides python3-jupyroot Obsoletes: python3-jupyroot < 6.40.00 -Requires: js-jsroot >= 7.11 +Requires: js-jsroot >= 7.11.1 %if %{?fedora}%{!?fedora:0} || %{?rhel}%{!?rhel:0} >= 10 # jupyter-notebook not available in RHEL/EPEL # some functionality missing @@ -1194,7 +1195,7 @@ access to http based storage such as webdav and S3. Summary: HTTP server extension for ROOT Requires: %{name}-core%{?_isa} = %{version}-%{release} Requires: %{name}-io%{?_isa} = %{version}-%{release} -Requires: js-jsroot >= 7.11 +Requires: js-jsroot >= 7.11.1 # Library split (net-httpsniff from net-http) Obsoletes: %{name}-net-http < 6.14.00 @@ -1341,41 +1342,22 @@ automatic hardware detection mechanism that this library contains. %package roofit-hs3 Summary: RooFit HS3 License: BSD-2-Clause +# roofit-jsoninterface was merged with roofit-hs3 +Provides: %{name}-roofit-jsoninterface = %{version}-%{release} +Obsoletes: %{name}-roofit-jsoninterface < 6.40.04 Requires: %{name}-core%{?_isa} = %{version}-%{release} Requires: %{name}-hist%{?_isa} = %{version}-%{release} Requires: %{name}-hist-factory%{?_isa} = %{version}-%{release} Requires: %{name}-matrix%{?_isa} = %{version}-%{release} Requires: %{name}-roofit%{?_isa} = %{version}-%{release} Requires: %{name}-roofit-core%{?_isa} = %{version}-%{release} -Requires: %{name}-roofit-jsoninterface%{?_isa} = %{version}-%{release} %description roofit-hs3 When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace. However, for the sake of interoperability with other statistical frameworks, and also ease of manipulation, it may be useful to store statistical models in text form. This library -sets out to achieve exactly that, exporting to and importing from JSON -and YML. - -%package roofit-jsoninterface -Summary: JSON interface to RooFit -License: BSD-2-Clause -Requires: %{name}-core%{?_isa} = %{version}-%{release} - -%description roofit-jsoninterface -The RooFit packages provide a toolkit for modeling the expected -distribution of events in a physics analysis. Models can be used to -perform likelihood fits, produce plots, and generate "toy Monte -Carlo" samples for various studies. The RooFit tools are integrated -with the object-oriented and interactive ROOT graphical environment. - -RooFit has been developed for the BaBar collaboration, a high energy -physics experiment at the Stanford Linear Accelerator Center, and is -primarily targeted to the high-energy physicists using the ROOT -analysis environment, but the general nature of the package make it -suitable for adoption in different disciplines as well. - -This package contains the JSON interface to RooFit. +sets out to achieve exactly that, exporting to and importing from JSON. %package roofit-codegen Summary: Code generation support for RooFit @@ -1963,6 +1945,7 @@ This package contains a library for histogramming in ROOT 7. %patch -P11 -p1 %patch -P12 -p1 %patch -P13 -p1 +%patch -P14 -p1 # Remove bundled sources in order to be sure they are not used # * ftgl @@ -2423,9 +2406,6 @@ popd # # - tutorial-visualisation-webcanv-fonts_ttf.cxx: # Requires web graphics -# -# - tmva-sofie-test-TestCladAutodiff -# Fails often... excluded="\ test-stressIOPlugins-http|\ test-stressIOPlugins-xroot|\ @@ -2461,8 +2441,7 @@ tutorial-machine_learning-tmva100_DataPreparation-py|\ test-webgui-ping|\ test-stressgraphics-firefox-skip3d|\ test-stressgraphics-svg|\ -tutorial-visualisation-webcanv-fonts_ttf.cxx|\ -tmva-sofie-test-TestCladAutodiff" +tutorial-visualisation-webcanv-fonts_ttf.cxx" %if %{?rhel}%{!?rhel:0} == 9 # - pyunittests-bindings-pyroot-cppyy-cppyy-test-datatypes @@ -3315,11 +3294,6 @@ fi %{_libdir}/%{name}/libRooFitHS3_rdict.pcm %dir %{_includedir}/%{name}/RooFitHS3 -%files roofit-jsoninterface -f includelist-roofit-jsoninterface -%{_libdir}/%{name}/libRooFitJSONInterface.* -%{_libdir}/%{name}/libRooFitJSONInterface_rdict.pcm -%dir %{_includedir}/%{name}/RooFit - %files roofit-codegen -f includelist-roofit-codegen %{_libdir}/%{name}/libRooFitCodegen.* %{_libdir}/%{name}/libRooFitCodegen_rdict.pcm @@ -3567,6 +3541,13 @@ fi %endif %changelog +* Sat Aug 29 2026 Mattias Ellert - 6.40.04-1 +- Update to 6.40.04 +- The root-roofit-jsoninterface package was merged with root-roofit-hs3 +- Fix calculation of kerning distances in generated PDF files +- Dropped patches: 1 +- New patches: 2 + * Wed Aug 26 2026 Mattias Ellert - 6.40.02-5 - Use the system civetweb library since it now supports web sockets - Drop the davix module for Fedora 45+ (davix will soon be retired) diff --git a/sources b/sources index f863ce7..a9df00b 100644 --- a/sources +++ b/sources @@ -1,3 +1,3 @@ -SHA512 (root-6.40.02.tar.xz) = 8a815a24db14a4160be00c21d275d5b527573b02844568b0f549f236c2b481c7ad3f43076a9e862a740c7b0784b2bfb54461d2c0d7eec2816f56d4428aa1c811 +SHA512 (root-6.40.04.tar.xz) = 674ea3de3f58d3bde15637925bec80984552cb5c4f6259ec68c2f7dc566014d9a3b2ee005e0f89b5cf20d9e5188dc5c9185d886a6701e315bb4771b068939282 SHA512 (root-testfiles.tar.xz) = 4bb7f8ca00b1427ba49f6428e9cfe5908a2ffdd2ce4134d3bc169a0629198352b5500fe0cf92c121d148e5af41f7016c96038a77794a4d37d94e6ec0a9d75670 SHA512 (clad-2.3.tar.gz) = 2d7e008ad88dd9e4b44d73e3187f8b1883e475845f5ac2d3b2c8e728508422e1e60e279940fefe322995751490f61f7d4768498c3660f9afa1206cc0e83bea33