diff --git a/Build/Cmake/CMakeLists.txt b/Build/Cmake/CMakeLists.txt index a67a9a51b..568526a73 100644 --- a/Build/Cmake/CMakeLists.txt +++ b/Build/Cmake/CMakeLists.txt @@ -530,6 +530,7 @@ option(ENABLE_WXWIDGETS "Build wxProfileDump GUI tool (requires wxWidg option(ENABLE_SHARED_LIBS "Build dynamic link libraries" ON) option(ENABLE_STATIC_LIBS "Build static libraries" ON) option(ENABLE_ICCXML "Build IccXML library support" ON) +option(ENABLE_ICCJSON "Build IccJSON library support" ON) option(ICC_TRACE_NAN_ENABLED "Enable tracing NaN inputs in debug builds" OFF) option(ICC_CLUT_DEBUG "Enable CLUT debugging support" OFF) option(ICC_ENABLE_ASSERTS "Enable ICC_ASSERT traps and debug assertions" OFF) @@ -1155,6 +1156,29 @@ ENDIF() # NOTE: IccXML and generated-header includes now propagated via # target_include_directories(PUBLIC) on IccProfLib2 and IccXML2 targets +# Ensure generated version headers are available globally +include_directories(${CMAKE_CURRENT_BINARY_DIR}/IccProfLib) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/IccXML) + +# +# JSON library (parallel to IccXML, uses nlohmann/json) +# +IF(ENABLE_ICCJSON) + message(STATUS "Adding subdirectory for IccJSON.") + add_subdirectory(IccJSON) + + # Set default link target for IccJSON (matches build configuration) + IF(ENABLE_SHARED_LIBS) + set(TARGET_LIB_ICCJSON IccJSON2 CACHE INTERNAL "Link target for IccJSON2") + ELSE() + set(TARGET_LIB_ICCJSON IccJSON2-static CACHE INTERNAL "Link target for IccJSON2") + ENDIF() + + # Ensure IccJSON headers are available globally + include_directories(${TOP_SOURCE_DIR}/IccJSON/IccLibJSON/) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/IccJSON) +ENDIF(ENABLE_ICCJSON) + # Diagnostic: Output current linker flags if(VERBOSE_CONFIG) message(STATUS "CMAKE_EXE_LINKER_FLAGS = ${CMAKE_EXE_LINKER_FLAGS}") @@ -1200,6 +1224,15 @@ IF(ENABLE_TOOLS) # Add XML-related tools ADD_SUBDIRECTORY(Tools/IccFromXml) ADD_SUBDIRECTORY(Tools/IccToXml) + + # Add JSON-related tools + IF(ENABLE_ICCJSON AND TARGET_LIB_ICCJSON) + message(STATUS "Adding Subdirectory IccFromJson.") + ADD_SUBDIRECTORY(Tools/IccFromJson) + message(STATUS "Adding Subdirectory IccToJson.") + ADD_SUBDIRECTORY(Tools/IccToJson) + ENDIF() + message(STATUS "Adding Subdirectory IccJpegDump.") ADD_SUBDIRECTORY(Tools/IccJpegDump) ADD_SUBDIRECTORY(Tools/IccApplyNamedCmm) ADD_SUBDIRECTORY(Tools/IccDumpProfile) @@ -1217,10 +1250,26 @@ IF(ENABLE_TOOLS) ADD_DEPENDENCIES(iccToXml ${TARGET_LIB_ICCXML}) ENDIF() + # Ensure IccJSON-dependent tools are built after IccJSON library (safety net) + IF(ENABLE_ICCJSON) + IF(TARGET iccFromJson AND TARGET ${TARGET_LIB_ICCJSON}) + ADD_DEPENDENCIES(iccFromJson ${TARGET_LIB_ICCJSON}) + ENDIF() + IF(TARGET iccToJson AND TARGET ${TARGET_LIB_ICCJSON}) + ADD_DEPENDENCIES(iccToJson ${TARGET_LIB_ICCJSON}) + ENDIF() + ENDIF() + -# PNG already found above; just add the tool -if(NOT PNG_FOUND) - message(FATAL_ERROR "PNG not found. Please install libpng-dev or install via vcpkg.") +# --- PNG (already found above) --- +message(STATUS "Checking for PNG...") + +if(PNG_FOUND) + message(STATUS "PNG Library : ${PNG_LIBRARIES}") + message(STATUS "PNG Include Directory : ${PNG_INCLUDE_DIRS}") + message(STATUS "PNG Version : ${PNG_VERSION}") +else() + message(FATAL_ERROR " PNG not found. Please install libpng-dev or install via vcpkg.") endif() if(VERBOSE_CONFIG) message(STATUS "Adding Subdirectory IccPngDump.") diff --git a/Build/Cmake/IccJSON/CMakeLists.txt b/Build/Cmake/IccJSON/CMakeLists.txt new file mode 100644 index 000000000..05f8f2570 --- /dev/null +++ b/Build/Cmake/IccJSON/CMakeLists.txt @@ -0,0 +1,141 @@ +################################################################################# +# IccJSON CMake Project Configuration | iccMAX Project +# Copyright (C) 2024-2026 The International Color Consortium. +# All rights reserved. +# +# Parallel implementation to IccXML using nlohmann/json (JSON for Modern C++). +# +################################################################################# + +SET( TARGET_NAME IccJSON2 ) + +SET( SRC_PATH ../../.. ) +SET( CFILES + ${SRC_PATH}/IccJSON/IccLibJSON/IccIoJson.cpp + ${SRC_PATH}/IccJSON/IccLibJSON/IccMpeJson.cpp + ${SRC_PATH}/IccJSON/IccLibJSON/IccMpeJsonFactory.cpp + ${SRC_PATH}/IccJSON/IccLibJSON/IccProfileJson.cpp + ${SRC_PATH}/IccJSON/IccLibJSON/IccTagJson.cpp + ${SRC_PATH}/IccJSON/IccLibJSON/IccTagJsonFactory.cpp + ${SRC_PATH}/IccJSON/IccLibJSON/IccUtilJson.cpp + ) + +IF(ENABLE_INSTALL_RIM) + SET( HEADERS_PUBLIC + ${SRC_PATH}/IccJSON/IccLibJSON/IccIoJson.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccJsonConfig.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccMpeJson.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccMpeJsonFactory.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccProfileJson.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccTagJson.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccTagJsonFactory.h + ${SRC_PATH}/IccJSON/IccLibJSON/IccUtilJson.h + ) + INSTALL( FILES + ${HEADERS_PUBLIC} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TARGET_INCLUDE_FOLDER}/${TARGET_NAME} ) +ENDIF(ENABLE_INSTALL_RIM) + +# Generate version header with git commit hash (reuses the project version string) +if(GIT_COMMIT_HASH) + set(ICCLIBXML_VERSION_STRING "${${PROJECT_UP_NAME}_VERSION}+${GIT_COMMIT_HASH}") +else() + set(ICCLIBXML_VERSION_STRING "${${PROJECT_UP_NAME}_VERSION}") +endif() + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/../../../IccJSON/IccLibJSON/IccLibJSONVer.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/IccLibJSONVer.h" + @ONLY +) + +include_directories("${CMAKE_CURRENT_BINARY_DIR}") + +SET(SOURCES ${CFILES}) + +IF(APPLE) + INCLUDE_DIRECTORIES(/Developer/Headers/FlatCarbon) + FIND_LIBRARY(CARBON_LIBRARY Carbon) + FIND_LIBRARY(IOKIT_LIBRARY IOKit) + MARK_AS_ADVANCED(CARBON_LIBRARY) + MARK_AS_ADVANCED(IOKIT_LIBRARY) + SET(EXTRA_LIBS_CS ${CARBON_LIBRARY} ${IOKIT_LIBRARY}) +ENDIF() + +# IccJSON has no IccXML dependency – all utilities are in IccProfLib + +IF(ENABLE_SHARED_LIBS) + ADD_LIBRARY(${TARGET_NAME} SHARED ${SOURCES}) + set_target_properties(${TARGET_NAME} + PROPERTIES + VERSION "${${PROJECT_UP_NAME}_VERSION}" + SOVERSION "${${PROJECT_UP_NAME}_MAJOR_VERSION}" + ) + + if(WIN32) + set_target_properties(${TARGET_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + endif() + + target_compile_features(${TARGET_NAME} PUBLIC cxx_std_17) + + target_include_directories(${TARGET_NAME} PUBLIC + $ + $ + $ + $ + $ + ) + + TARGET_LINK_LIBRARIES(${TARGET_NAME} PUBLIC ${TARGET_LIB_ICCPROFLIB} nlohmann_json::nlohmann_json ${EXTRA_LIBS} ${EXTRA_LIBS_CS}) + + IF(ENABLE_INSTALL_RIM) + INSTALL(TARGETS ${TARGET_NAME} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + ENDIF() +ENDIF() + +IF(ENABLE_STATIC_LIBS) + ADD_LIBRARY(${TARGET_NAME}-static STATIC ${SOURCES}) + SET_TARGET_PROPERTIES(${TARGET_NAME}-static PROPERTIES OUTPUT_NAME "${TARGET_NAME}-static") + target_compile_features(${TARGET_NAME}-static PUBLIC cxx_std_17) + + target_include_directories(${TARGET_NAME}-static PUBLIC + $ + $ + $ + $ + $ + ) + + TARGET_LINK_LIBRARIES(${TARGET_NAME}-static PUBLIC ${TARGET_LIB_ICCPROFLIB} nlohmann_json::nlohmann_json ${EXTRA_LIBS} ${EXTRA_LIBS_CS}) + + IF(ENABLE_INSTALL_RIM) + INSTALL(TARGETS ${TARGET_NAME}-static + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + ENDIF() + + IF(WIN32 AND ENABLE_SHARED_LIBS) + ADD_CUSTOM_COMMAND(TARGET ${TARGET_NAME}-static POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$/${TARGET_NAME}.lib" + COMMENT "Aliasing ${TARGET_NAME}-static.lib to ${TARGET_NAME}.lib for MSVC compatibility") + ENDIF() + + IF(NOT ENABLE_SHARED_LIBS) + ADD_LIBRARY(${TARGET_NAME} ALIAS ${TARGET_NAME}-static) + ENDIF() +ENDIF() + +# Resolve linking target for parent scope +IF(ENABLE_SHARED_LIBS) + SET(TARGET_LIB ${TARGET_NAME}) +ELSE() + SET(TARGET_LIB ${TARGET_NAME}-static) +ENDIF() + +SET(TARGET_LIB ${TARGET_LIB} PARENT_SCOPE) diff --git a/Build/Cmake/Tools/IccFromJson/CMakeLists.txt b/Build/Cmake/Tools/IccFromJson/CMakeLists.txt new file mode 100644 index 000000000..768e550f6 --- /dev/null +++ b/Build/Cmake/Tools/IccFromJson/CMakeLists.txt @@ -0,0 +1,26 @@ +################################################################################# +# iccFromJson CMake Configuration | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. +# All rights reserved. +# +# Converts a JSON ICC profile to a binary ICC profile file. +################################################################################# + +SET( SRC_PATH ../../../.. ) +SET( SOURCES ${SRC_PATH}/IccJSON/CmdLine/IccFromJson/IccFromJson.cpp ) +SET( TARGET_NAME iccFromJson ) + +ADD_EXECUTABLE( ${TARGET_NAME} ${SOURCES} ) + +# Ensure IccJSON2 is built before linking +IF(ENABLE_SHARED_LIBS) + ADD_DEPENDENCIES( ${TARGET_NAME} IccJSON2 ) +ELSE() + ADD_DEPENDENCIES( ${TARGET_NAME} IccJSON2-static ) +ENDIF() + +TARGET_LINK_LIBRARIES( ${TARGET_NAME} ${TARGET_LIB_ICCJSON} ) + +IF(ENABLE_INSTALL_RIM) + INSTALL (TARGETS ${TARGET_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) +ENDIF(ENABLE_INSTALL_RIM) diff --git a/Build/Cmake/Tools/IccToJson/CMakeLists.txt b/Build/Cmake/Tools/IccToJson/CMakeLists.txt new file mode 100644 index 000000000..30b80ccd4 --- /dev/null +++ b/Build/Cmake/Tools/IccToJson/CMakeLists.txt @@ -0,0 +1,26 @@ +################################################################################# +# iccToJson CMake Configuration | iccDEV Project +# Copyright (C) 2024-2026 The International Color Consortium. +# All rights reserved. +# +# Converts a binary ICC profile to a JSON file. +################################################################################# + +SET( SRC_PATH ../../../.. ) +SET( SOURCES ${SRC_PATH}/IccJSON/CmdLine/IccToJson/IccToJson.cpp ) +SET( TARGET_NAME iccToJson ) + +ADD_EXECUTABLE( ${TARGET_NAME} ${SOURCES} ) + +# Ensure IccJSON2 is built before linking +IF(ENABLE_SHARED_LIBS) + ADD_DEPENDENCIES( ${TARGET_NAME} IccJSON2 ) +ELSE() + ADD_DEPENDENCIES( ${TARGET_NAME} IccJSON2-static ) +ENDIF() + +TARGET_LINK_LIBRARIES( ${TARGET_NAME} ${TARGET_LIB_ICCJSON} ) + +IF(ENABLE_INSTALL_RIM) + INSTALL (TARGETS ${TARGET_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) +ENDIF(ENABLE_INSTALL_RIM) diff --git a/IccJSON/CmdLine/IccFromJson/IccFromJson.cpp b/IccJSON/CmdLine/IccFromJson/IccFromJson.cpp new file mode 100644 index 000000000..f8e7ec4ca --- /dev/null +++ b/IccJSON/CmdLine/IccFromJson/IccFromJson.cpp @@ -0,0 +1,74 @@ +// IccFromJson.cpp : Convert a JSON ICC profile to a binary ICC profile file. +// + +#include +#include "IccTagJsonFactory.h" +#include "IccMpeJsonFactory.h" +#include "IccProfileJson.h" +#include "IccIO.h" +#include "IccUtil.h" +#include "IccProfLibVer.h" +#include "IccLibJSONVer.h" +#include + +int main(int argc, char* argv[]) +{ + if (argc <= 2) { + printf("IccFromJson built with IccProfLib Version " ICCPROFLIBVER ", IccLibJSON Version " ICCLIBJSONVER "\n\n"); + printf("Usage: IccFromJson json_file saved_profile_file {-noid}\n"); + return 0; + } + + CIccTagCreator::PushFactory(new CIccTagJsonFactory()); + CIccMpeCreator::PushFactory(new CIccMpeJsonFactory()); + + CIccProfileJson profile; + std::string reason; + + bool bNoId = false; + for (int i = 3; i < argc; i++) { + if (!stricmp(argv[i], "-noid")) + bNoId = true; + } + + if (!profile.LoadJson(argv[1], &reason)) { + printf("%s", reason.c_str()); + printf("Unable to Parse '%s'\n", argv[1]); + return -1; + } + + std::string valid_report; + + if (profile.Validate(valid_report) <= icValidateWarning) { + int i; + for (i = 0; i < 16; i++) { + if (profile.m_Header.profileID.ID8[i]) + break; + } + if (SaveIccProfile(argv[2], &profile, bNoId ? icNeverWriteID : (i < 16 ? icAlwaysWriteID : icVersionBasedID))) { + printf("Profile parsed and saved correctly\n"); + } + else { + printf("Unable to save profile as '%s'\n", argv[2]); + return -1; + } + } + else { + int i; + for (i = 0; i < 16; i++) { + if (profile.m_Header.profileID.ID8[i]) + break; + } + if (SaveIccProfile(argv[2], &profile, bNoId ? icNeverWriteID : (i < 16 ? icAlwaysWriteID : icVersionBasedID))) { + printf("Profile parsed. Profile is invalid, but saved correctly\n"); + } + else { + printf("Unable to save profile - profile is invalid!\n"); + return -1; + } + printf("%s", valid_report.c_str()); + } + + printf("\n"); + return 0; +} diff --git a/IccJSON/CmdLine/IccToJson/IccToJson.cpp b/IccJSON/CmdLine/IccToJson/IccToJson.cpp new file mode 100644 index 000000000..57c0e4587 --- /dev/null +++ b/IccJSON/CmdLine/IccToJson/IccToJson.cpp @@ -0,0 +1,71 @@ +// IccToJson.cpp : Convert a binary ICC profile to a JSON file. +// + +#include +#include "IccTagJsonFactory.h" +#include "IccMpeJsonFactory.h" +#include "IccProfileJson.h" +#include "IccIO.h" +#include "IccProfLibVer.h" +#include "IccLibJSONVer.h" +#include + +int main(int argc, char* argv[]) +{ + if (argc <= 2) { + printf("IccToJson built with IccProfLib Version " ICCPROFLIBVER ", IccLibJSON Version " ICCLIBJSONVER "\n\n"); + printf("Usage: IccToJson src_icc_profile dest_json_file {-indent=N}\n"); + printf(" -indent=N pretty-print with N spaces of indentation (default: 2)\n"); + return 0; + } + + CIccTagCreator::PushFactory(new CIccTagJsonFactory()); + CIccMpeCreator::PushFactory(new CIccMpeJsonFactory()); + + int indent = 2; + for (int i = 3; i < argc; i++) { + if (!strncmp(argv[i], "-indent=", 8)) + indent = atoi(argv[i] + 8); + } + + CIccProfileJson profile; + CIccFileIO srcIO; + + if (!srcIO.Open(argv[1], "r")) { + printf("Unable to open '%s'\n", argv[1]); + return -1; + } + + if (!profile.Read(&srcIO)) { + printf("Unable to read '%s'\n", argv[1]); + return -1; + } + + std::string jsonStr; + try { + if (!profile.ToJson(jsonStr, indent)) { + printf("Unable to convert '%s' to JSON\n", argv[1]); + return -1; + } + } + catch (const std::exception &e) { + printf("JSON serialization error for '%s': %s\n", argv[1], e.what()); + return -1; + } + + std::ofstream outFile(argv[2]); + if (!outFile.is_open()) { + printf("Unable to open '%s' for writing\n", argv[2]); + return -1; + } + + outFile << jsonStr; + if (outFile.fail()) { + printf("Unable to write '%s'\n", argv[2]); + return -1; + } + outFile.close(); + + printf("JSON successfully created\n"); + return 0; +} diff --git a/IccJSON/IccLibJSON/IccIoJson.cpp b/IccJSON/IccLibJSON/IccIoJson.cpp new file mode 100644 index 000000000..a78c8865d --- /dev/null +++ b/IccJSON/IccLibJSON/IccIoJson.cpp @@ -0,0 +1,93 @@ +/** @file +File: IccIoJson.cpp + +Contains: Pluggable file-IO interface for IccJSON (mirrors IccIoXml.cpp) + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccIoJson.h" + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +CIccIO *CIccJsonStandardFileIO::OpenFile(const icChar *szFilename, const char *szAttr) +{ + CIccFileIO *file = new CIccFileIO(); + if (!file->Open(szFilename, szAttr)) { + delete file; + return nullptr; + } + return file; +} + +static CIccJsonStandardFileIO g_IccJsonStandardFileIO; +static IIccJsonOpenFileIO *g_pIccJsonFileIO = &g_IccJsonStandardFileIO; + +CIccIO *IccJsonOpenFileIO(const icChar *szFilename, const char *szAttr) +{ + if (g_pIccJsonFileIO) + return g_pIccJsonFileIO->OpenFile(szFilename, szAttr); + return nullptr; +} + +void IccJsonSetOpenFileIO(IIccJsonOpenFileIO *pOpenIO) +{ + g_pIccJsonFileIO = pOpenIO; +} + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif diff --git a/IccJSON/IccLibJSON/IccIoJson.h b/IccJSON/IccLibJSON/IccIoJson.h new file mode 100644 index 000000000..349274fa5 --- /dev/null +++ b/IccJSON/IccLibJSON/IccIoJson.h @@ -0,0 +1,105 @@ +/** @file +File: IccIoJson.h + +Contains: Pluggable file-IO interface for IccJSON (mirrors IccIoXml.h) + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCIOJSON_H +#define _ICCIOJSON_H + +#include "IccIO.h" + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +/** + ************************************************************************** + * Type: Interface + * + * Purpose: Provides pluggable file-IO for IccJSON (same contract as + * IIccOpenFileIO in IccIoXml.h but independent state). + ************************************************************************** + */ +class IIccJsonOpenFileIO +{ +public: + virtual CIccIO *OpenFile(const icChar *szFilename, const char *szAttr) = 0; +}; + + +/** + ************************************************************************** + * Type: Class + * + * Purpose: Default IIccJsonOpenFileIO implementation using CIccFileIO. + ************************************************************************** + */ +class ICCPROFLIB_API CIccJsonStandardFileIO : public IIccJsonOpenFileIO +{ +public: + CIccJsonStandardFileIO() {} + virtual CIccIO *OpenFile(const icChar *szFilename, const char *szAttr); +}; + +void ICCPROFLIB_API IccJsonSetOpenFileIO(IIccJsonOpenFileIO *pOpenIO); +CIccIO* ICCPROFLIB_API IccJsonOpenFileIO(const icChar *szFilename, const char *szAttr); + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif + +#endif // _ICCIOJSON_H diff --git a/IccJSON/IccLibJSON/IccJsonConfig.h b/IccJSON/IccLibJSON/IccJsonConfig.h new file mode 100644 index 000000000..5888d2928 --- /dev/null +++ b/IccJSON/IccLibJSON/IccJsonConfig.h @@ -0,0 +1,77 @@ +/** @file + File: IccJsonConfig.h + + Contains: Configuration for ICC/JSON processing + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCJSONCONFIG_H +#define _ICCJSONCONFIG_H + +// icConvertType is shared with IccXmlConfig.h; only define if not already defined +#ifndef _ICCXMLCONFIG_H +typedef enum { + icConvert8Bit = 0, + icConvert16Bit, + icConvertFloat, + icConvertVariable +} icConvertType; +#endif + +#define icJsonHalfFmt "%.8f" +#define icJsonFloatFmt "%.12f" +#define icJsonDoubleFmt "%.24lf" + +#endif // _ICCJSONCONFIG_H diff --git a/IccJSON/IccLibJSON/IccLibJSONVer.h.in b/IccJSON/IccLibJSON/IccLibJSONVer.h.in new file mode 100644 index 000000000..ac15dfa26 --- /dev/null +++ b/IccJSON/IccLibJSON/IccLibJSONVer.h.in @@ -0,0 +1,4 @@ +#ifndef ICCLIBJSONVER +#define ICCLIBJSONVER "@ICCLIBXML_VERSION_STRING@" +#define ICCPROFLIBJSONVER "@ICCLIBXML_VERSION_STRING@" +#endif diff --git a/IccJSON/IccLibJSON/IccMpeJson.cpp b/IccJSON/IccLibJSON/IccMpeJson.cpp new file mode 100644 index 000000000..833f094fc --- /dev/null +++ b/IccJSON/IccLibJSON/IccMpeJson.cpp @@ -0,0 +1,2240 @@ +/** @file + File: IccMpeJson.cpp + + Contains: Implementation of ICC multi-process element JSON format conversions + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccMpeJson.h" +#include "IccTagJson.h" +#include "IccIoJson.h" +#include "IccUtilJson.h" +#include "IccUtil.h" +#include "IccSolve.h" +#include "IccCAM.h" +#include "IccMpeFactory.h" +#include +#include +#include +#include + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +// =========================================================================== +// CIccMpeJsonUnknown +// =========================================================================== + +bool CIccMpeJsonUnknown::ToJson(IccJson &j) +{ + j["unknownData"] = icJsonDumpHexData(m_pData, m_nSize); + return true; +} + +bool CIccMpeJsonUnknown::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + m_nInputChannels = (icUInt16Number)nIn; + m_nOutputChannels = (icUInt16Number)nOut; + if (j.contains("unknownData") && j["unknownData"].is_string()) { + std::string hex = j["unknownData"].get(); + m_nSize = icJsonGetHexDataSize(hex.c_str()); + if (m_pData) { delete[] m_pData; m_pData = NULL; } + if (m_nSize) { + m_pData = new icUInt8Number[m_nSize]; + icJsonGetHexData(m_pData, hex.c_str(), m_nSize); + } + } + return true; +} + +// =========================================================================== +// Segment position helpers (JSON has no native infinity — use strings) +// =========================================================================== + +// Emit pos as a JSON number for finite values, or a string for ±infinity. +static void icJsonSetSegPos(IccJson &j, const char *field, icFloatNumber pos) +{ + if (pos == icMinFloat32Number) + j[field] = "-infinity"; + else if (pos == icMaxFloat32Number) + j[field] = "+infinity"; + else + j[field] = (double)pos; +} + +static icFloatNumber icJsonSegPosFromStr(const std::string &s) +{ + if (s.size() >= 4 && s.substr(s.size()-3) == "inf") return icMaxFloat32Number; + if (!s.empty() && s[0] == '-' && s.size() >= 4) return icMinFloat32Number; + return (icFloatNumber)atof(s.c_str()); +} + +static icFloatNumber icJsonSegPosFromJson(const IccJson &jv) +{ + if (jv.is_string()) return icJsonSegPosFromStr(jv.get()); + if (jv.is_number()) return (icFloatNumber)jv.get(); + return 0.0f; +} + +static int icFormulaCurveSegParamCount(int funcType) +{ + switch (funcType) { + case 0: return 4; + case 1: case 2: case 3: case 4: return 5; + case 5: case 7: return 6; + case 6: return 7; + default: return -1; + } +} + +// --------------------------------------------------------------------------- +// Local JSON-aware derived segment classes (need access to protected members) +// --------------------------------------------------------------------------- + +class CIccFormulaCurveSegmentJson : public CIccFormulaCurveSegment +{ +public: + CIccFormulaCurveSegmentJson(icFloatNumber start, icFloatNumber end) + : CIccFormulaCurveSegment(start, end) {} + CIccFormulaCurveSegmentJson(const CIccFormulaCurveSegment *p) + : CIccFormulaCurveSegment(*p) {} + + bool ToJson(IccJson &j) const { + j["type"] = "FormulaSegment"; + j["functionType"] = (int)m_nFunctionType; + if (m_nReserved) + j["reserved"] = (int)m_nReserved; + if (m_nReserved2) + j["reserved2"] = (int)m_nReserved2; + IccJson params = IccJson::array(); + for (int i = 0; i < m_nParameters; i++) + params.push_back((double)m_params[i]); + j["parameters"] = params; + return true; + } + + bool ParseJson(const IccJson &j, std::string &parseStr) { + int funcType = 0, reserved = 0, reserved2 = 0; + jGetValue(j, "functionType", funcType); + jGetValue(j, "reserved", reserved); + jGetValue(j, "reserved2", reserved2); + m_nFunctionType = (icUInt16Number)funcType; + m_nReserved = (icUInt32Number)reserved; + m_nReserved2 = (icUInt16Number)reserved2; + + int nParams = icFormulaCurveSegParamCount(funcType); + if (nParams < 0) { + parseStr += "Unsupported FunctionType in FormulaSegment\n"; + return false; + } + m_nParameters = (icUInt8Number)nParams; + + if (m_params) { free(m_params); m_params = nullptr; } + if (nParams > 0) { + m_params = (icFloatNumber*)malloc(nParams * sizeof(icFloatNumber)); + if (!m_params) return false; + for (int i = 0; i < nParams; i++) m_params[i] = 0.0f; + if (jsonExistsField(j, "parameters") && j["parameters"].is_array()) { + int cnt = std::min(nParams, (int)j["parameters"].size()); + for (int i = 0; i < cnt; i++) + m_params[i] = (icFloatNumber)j["parameters"][i].get(); + } + } + return true; + } +}; + + +class CIccSampledCurveSegmentJson : public CIccSampledCurveSegment +{ +public: + CIccSampledCurveSegmentJson(icFloatNumber start, icFloatNumber end) + : CIccSampledCurveSegment(start, end) {} + CIccSampledCurveSegmentJson(const CIccSampledCurveSegment *p) + : CIccSampledCurveSegment(*p) {} + + bool ToJson(IccJson &j) const { + j["type"] = "SampledSegment"; + IccJson samples = IccJson::array(); + for (icUInt32Number i = 0; i < m_nCount; i++) + samples.push_back((double)m_pSamples[i]); + j["samples"] = samples; + return true; + } + + bool ParseJson(const IccJson &j, std::string &parseStr) { + if (!jsonExistsField(j, "samples") || !j["samples"].is_array()) { + parseStr += "Missing samples in SampledSegment\n"; + return false; + } + const IccJson &arr = j["samples"]; + if (!SetSize((icUInt32Number)arr.size())) + return false; + for (icUInt32Number i = 0; i < (icUInt32Number)arr.size(); i++) + m_pSamples[i] = (icFloatNumber)arr[i].get(); + return true; + } +}; + +// =========================================================================== +// CIccSegmentedCurveJson +// =========================================================================== + +bool CIccSegmentedCurveJson::ToJson(IccJson &j) +{ + IccJson segs = IccJson::array(); + for (CIccCurveSegment *pSeg : *m_list) { + if (!pSeg) continue; + IccJson jSeg; + icJsonSetSegPos(jSeg, "start", pSeg->StartPoint()); + icJsonSetSegPos(jSeg, "end", pSeg->EndPoint()); + + if (pSeg->GetType() == icSigFormulaCurveSeg) { + CIccFormulaCurveSegmentJson js(static_cast(pSeg)); + if (!js.ToJson(jSeg)) return false; + } + else if (pSeg->GetType() == icSigSampledCurveSeg) { + CIccSampledCurveSegmentJson js(static_cast(pSeg)); + if (!js.ToJson(jSeg)) return false; + } + else { + return false; + } + segs.push_back(jSeg); + } + j["segments"] = segs; + return true; +} + +bool CIccSegmentedCurveJson::ParseJson(const IccJson &j, std::string &parseStr) +{ + m_list->clear(); + + if (!jsonExistsField(j, "segments") || !j["segments"].is_array()) + return true; + + for (const IccJson &jSeg : j["segments"]) { + std::string type; + jGetString(jSeg, "type", type); + // start/end may be strings ("-infinity"/"+infinity") or numbers + icFloatNumber start = jSeg.contains("start") ? icJsonSegPosFromJson(jSeg["start"]) : 0.0f; + icFloatNumber end = jSeg.contains("end") ? icJsonSegPosFromJson(jSeg["end"]) : 0.0f; + + if (type == "FormulaSegment") { + CIccFormulaCurveSegmentJson *pSeg = new CIccFormulaCurveSegmentJson(start, end); + if (!pSeg->ParseJson(jSeg, parseStr)) { delete pSeg; return false; } + m_list->push_back(pSeg); + } + else if (type == "SampledSegment") { + CIccSampledCurveSegmentJson *pSeg = new CIccSampledCurveSegmentJson(start, end); + if (!pSeg->ParseJson(jSeg, parseStr)) { delete pSeg; return false; } + m_list->push_back(pSeg); + } + else { + parseStr += "Unknown segment type: " + type + "\n"; + return false; + } + } + return true; +} + +// --------------------------------------------------------------------------- +// Local JSON-aware derived curve classes (need access to protected members) +// --------------------------------------------------------------------------- + +class CIccSingleSampledCurveJson : public CIccSingleSampledCurve +{ +public: + CIccSingleSampledCurveJson(icFloatNumber first = 0.0f, icFloatNumber last = 1.0f) + : CIccSingleSampledCurve(first, last) {} + CIccSingleSampledCurveJson(const CIccSingleSampledCurve *p) + : CIccSingleSampledCurve(*p) {} + + bool ToJson(IccJson &j) const { + j["type"] = "SingleSampledCurve"; + j["firstEntry"] = (double)m_firstEntry; + j["lastEntry"] = (double)m_lastEntry; + j["storageType"] = (int)m_storageType; + j["extensionType"] = (int)m_extensionType; + if (m_nReserved) + j["reserved"] = (int)m_nReserved; + IccJson samples = IccJson::array(); + for (icUInt32Number i = 0; i < m_nCount; i++) + samples.push_back((double)m_pSamples[i]); + j["samples"] = samples; + return true; + } + + bool ParseJson(const IccJson &j, std::string &parseStr) { + icFloatNumber first = j.contains("firstEntry") ? (icFloatNumber)j["firstEntry"].get() : 0.0f; + icFloatNumber last = j.contains("lastEntry") ? (icFloatNumber)j["lastEntry"].get() : 1.0f; + SetRange(first, last); + + int storageType = (int)icValueTypeFloat32; + int extensionType = (int)icClipSingleSampledCurve; + int reserved = 0; + jGetValue(j, "storageType", storageType); + jGetValue(j, "extensionType", extensionType); + jGetValue(j, "reserved", reserved); + SetStorageType((icUInt16Number)storageType); + SetExtensionType((icUInt16Number)extensionType); + m_nReserved = (icUInt32Number)reserved; + + if (!j.contains("samples") || !j["samples"].is_array()) { + parseStr += "Missing samples in SingleSampledCurve\n"; + return false; + } + const IccJson &arr = j["samples"]; + if (!SetSize((icUInt32Number)arr.size())) + return false; + for (icUInt32Number i = 0; i < (icUInt32Number)arr.size(); i++) + m_pSamples[i] = (icFloatNumber)arr[i].get(); + return true; + } +}; + + +class CIccSampledCalculatorCurveJson : public CIccSampledCalculatorCurve +{ +public: + CIccSampledCalculatorCurveJson(icFloatNumber first = 0.0f, icFloatNumber last = 1.0f) + : CIccSampledCalculatorCurve(first, last) {} + CIccSampledCalculatorCurveJson(const CIccSampledCalculatorCurve *p) + : CIccSampledCalculatorCurve(*p) {} + + bool ToJson(IccJson &j) const { + j["type"] = "SampledCalculatorCurve"; + j["firstEntry"] = (double)m_firstEntry; + j["lastEntry"] = (double)m_lastEntry; + j["extensionType"] = (int)m_extensionType; + j["desiredSize"] = (int)m_nDesiredSize; + if (m_nReserved2) + j["reserved2"] = (int)m_nReserved2; + if (m_pCalc) { + IIccExtensionMpe *pExt = m_pCalc->GetExtension(); + if (pExt && !strcmp(pExt->GetExtClassName(), "CIccMpeJson")) { + IccJson jCalc; + jCalc["inputChannels"] = (int)m_pCalc->NumInputChannels(); + jCalc["outputChannels"] = (int)m_pCalc->NumOutputChannels(); + static_cast(pExt)->ToJson(jCalc); + j["calculator"] = jCalc; + } + else { + return false; + } + } + return true; + } + + bool ParseJson(const IccJson &j, std::string &parseStr) { + icFloatNumber first = j.contains("firstEntry") ? (icFloatNumber)j["firstEntry"].get() : 0.0f; + icFloatNumber last = j.contains("lastEntry") ? (icFloatNumber)j["lastEntry"].get() : 1.0f; + SetRange(first, last); + + int extensionType = 0, desiredSize = 256, reserved2 = 0; + jGetValue(j, "extensionType", extensionType); + jGetValue(j, "desiredSize", desiredSize); + jGetValue(j, "reserved2", reserved2); + SetExtensionType((icUInt16Number)extensionType); + SetRecommendedSize((icUInt32Number)desiredSize); + m_nReserved2 = (icUInt16Number)reserved2; + + if (j.contains("calculator") && j["calculator"].is_object()) { + CIccMpeJsonCalculator *pCalc = new CIccMpeJsonCalculator(); + if (!pCalc->ParseJson(j["calculator"], parseStr)) { + delete pCalc; + return false; + } + if (!SetCalculator(pCalc)) { + delete pCalc; + parseStr += "SetCalculator failed in SampledCalculatorCurve\n"; + return false; + } + } + return true; + } +}; + + +// Dispatch a single CurveSet curve to JSON +static bool icToJsonCurve(IccJson &j, icCurveSetCurvePtr pCurve) +{ + if (!pCurve) + return false; + + if (pCurve->GetType() == icSigSegmentedCurve) { + j["type"] = "SegmentedCurve"; + CIccSegmentedCurveJson scJson(static_cast(pCurve)); + return scJson.ToJson(j); + } + else if (pCurve->GetType() == icSigSingleSampledCurve) { + CIccSingleSampledCurveJson sscJson(static_cast(pCurve)); + return sscJson.ToJson(j); + } + else if (pCurve->GetType() == icSigSampledCalculatorCurve) { + CIccSampledCalculatorCurveJson sccJson(static_cast(pCurve)); + return sccJson.ToJson(j); + } + return false; +} + +// Create a CurveSet curve from a JSON object +static icCurveSetCurvePtr icFromJsonCurve(const IccJson &jCurve, std::string &parseStr) +{ + std::string type; + jGetString(jCurve, "type", type); + + if (type == "SegmentedCurve") { + CIccSegmentedCurveJson *pCurve = new CIccSegmentedCurveJson(); + if (pCurve->ParseJson(jCurve, parseStr)) + return pCurve; + delete pCurve; + } + else if (type == "SingleSampledCurve") { + CIccSingleSampledCurveJson *pCurve = new CIccSingleSampledCurveJson(); + if (pCurve->ParseJson(jCurve, parseStr)) + return pCurve; + delete pCurve; + } + else if (type == "SampledCalculatorCurve") { + CIccSampledCalculatorCurveJson *pCurve = new CIccSampledCalculatorCurveJson(); + if (pCurve->ParseJson(jCurve, parseStr)) + return pCurve; + delete pCurve; + } + else { + parseStr += "Unknown curve type: " + type + "\n"; + } + return nullptr; +} + + +// =========================================================================== +// CIccMpeJsonCurveSet +// =========================================================================== + +bool CIccMpeJsonCurveSet::ToJson(IccJson &j) +{ + int nChannels = NumInputChannels(); + j["inputChannels"] = nChannels; + j["outputChannels"] = NumOutputChannels(); + + IccJson curves = IccJson::array(); + for (int i = 0; i < nChannels; i++) { + // Check for duplicate (shared pointer with an earlier slot) + int dupIdx = -1; + for (int k = 0; k < i; k++) { + if (m_curve[i] == m_curve[k]) { dupIdx = k; break; } + } + + IccJson jCurve; + if (dupIdx >= 0) { + jCurve["type"] = "DuplicateCurve"; + jCurve["index"] = dupIdx; + } + else if (!icToJsonCurve(jCurve, m_curve[i])) { + return false; + } + curves.push_back(jCurve); + } + j["curves"] = curves; + return true; +} + +bool CIccMpeJsonCurveSet::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + + if (!nIn || nIn != nOut) { + parseStr += "Invalid inputChannels or outputChannels in CurveSetElement\n"; + return false; + } + + icUInt16Number nChannels = (icUInt16Number)nIn; + SetSize(nChannels); + + if (!j.contains("curves") || !j["curves"].is_array()) { + parseStr += "Missing curves array in CurveSetElement\n"; + return false; + } + + const IccJson &jCurves = j["curves"]; + int nIndex = 0; + for (const IccJson &jCurve : jCurves) { + if (nIndex >= nChannels) { + parseStr += "Too many curves in CurveSetElement\n"; + return false; + } + + std::string type; + jGetString(jCurve, "type", type); + + if (type == "DuplicateCurve") { + int dupIdx = -1; + jGetValue(jCurve, "index", dupIdx); + if (dupIdx < 0 || dupIdx >= nIndex) { + parseStr += "Invalid index for DuplicateCurve\n"; + return false; + } + m_curve[nIndex] = m_curve[dupIdx]; + } + else { + icCurveSetCurvePtr pCurve = icFromJsonCurve(jCurve, parseStr); + if (!pCurve) + return false; + if (!SetCurve(nIndex, pCurve)) + return false; + } + nIndex++; + } + + return (nIndex == nChannels); +} + +// =========================================================================== +// CIccMpeJsonTintArray +// =========================================================================== + +bool CIccMpeJsonTintArray::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + + if (m_Array) { + const icChar *typeName = CIccTagCreator::GetTagTypeSigName(m_Array->GetType()); + if (typeName) { + j["arrayType"] = typeName; + } + else { + char sigBuf[32]; + icGetSigStr(sigBuf, sizeof(sigBuf), m_Array->GetType()); + j["arrayType"] = sigBuf; + } + + IIccExtensionTag *pExt = m_Array->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) + return false; + if (!static_cast(pExt)->ToJson(j)) + return false; + } + return true; +} + +bool CIccMpeJsonTintArray::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + m_nInputChannels = (icUInt16Number)nIn; + m_nOutputChannels = (icUInt16Number)nOut; + + // Determine array element type (default: float32) + std::string arrayTypeName; + jGetString(j, "arrayType", arrayTypeName); + icTagTypeSignature typeSig = arrayTypeName.empty() + ? icSigFloat32ArrayType + : CIccTagCreator::GetTagTypeNameSig(arrayTypeName.c_str()); + if (typeSig == icSigUnknownType) + typeSig = icSigFloat32ArrayType; + + // Create JSON-aware tag for the array data + CIccTag *pTag = CIccTagCreator::CreateTag(typeSig); + if (!pTag || !pTag->IsNumArrayType()) { + parseStr += "Invalid arrayType for TintArrayElement\n"; + delete pTag; + return false; + } + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) { + parseStr += "arrayType does not support JSON for TintArrayElement\n"; + delete pTag; + return false; + } + CIccTagJson *pTagJson = static_cast(pExt); + + // External file + if (j.contains("file") && j["file"].is_string()) { + std::string filename = j["file"].get(); + std::string format = "text"; + jGetString(j, "format", format); + + CIccIO *pIO = IccJsonOpenFileIO(filename.c_str(), "rb"); + if (!pIO) { + parseStr += "Cannot open TintArray file: " + filename + "\n"; + delete pTag; + return false; + } + + if (format == "text") { + size_t fileLen = pIO->GetLength(); + char *buf = new char[fileLen + 1]; + icUInt32Number nRead = pIO->Read8(buf, (icUInt32Number)fileLen); + buf[nRead] = '\0'; + delete pIO; + + IccJson jArr = IccJson::array(); + const char *p = buf; + const char *end = buf + nRead; + while (p < end) { + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',')) + p++; + if (p >= end) break; + char *next = nullptr; + double v = strtod(p, &next); + if (!next || next == p) break; + jArr.push_back(v); + p = next; + } + delete[] buf; + + IccJson jValues; + jValues["values"] = jArr; + if (!pTagJson->ParseJson(jValues, parseStr)) { delete pTag; return false; } + } + else if (format == "binary") { + size_t fileLen = pIO->GetLength(); + icUInt32Number nVals = (icUInt32Number)(fileLen / sizeof(icFloat32Number)); + IccJson jArr = IccJson::array(); + for (icUInt32Number i = 0; i < nVals; i++) { + icFloat32Number v = 0.0f; + if (!pIO->ReadFloat32Float(&v)) break; + jArr.push_back((double)v); + } + delete pIO; + + IccJson jValues; + jValues["values"] = jArr; + if (!pTagJson->ParseJson(jValues, parseStr)) { delete pTag; return false; } + } + else { + delete pIO; + parseStr += "Unsupported format '" + format + "' for TintArrayElement file\n"; + delete pTag; + return false; + } + } + else { + // Inline: the tag's ParseJson reads "values" from j + if (!pTagJson->ParseJson(j, parseStr)) { delete pTag; return false; } + } + + SetArray(static_cast(pTag)); + return true; +} + +// =========================================================================== +// CIccJsonToneMapFunc +// =========================================================================== + +CIccToneMapFunc* CIccJsonToneMapFunc::NewCopy() const +{ + return new CIccJsonToneMapFunc(*this); +} + +bool CIccJsonToneMapFunc::ToJson(IccJson &j) +{ + j["functionType"] = (int)m_nFunctionType; + if (m_nReserved2) + j["reserved2"] = (int)m_nReserved2; + IccJson params = IccJson::array(); + for (icUInt8Number i = 0; i < m_nParameters; i++) + params.push_back((double)m_params[i]); + j["parameters"] = params; + return true; +} + +bool CIccJsonToneMapFunc::ParseJson(const IccJson &j, std::string &parseStr) +{ + int funcType = 0, reserved2 = 0; + jGetValue(j, "functionType", funcType); + jGetValue(j, "reserved2", reserved2); + m_nFunctionType = (icUInt16Number)funcType; + m_nReserved2 = (icUInt16Number)reserved2; + + switch (m_nFunctionType) { + case 0: m_nParameters = 3; break; + default: + parseStr += "Unsupported functionType in ToneMapFunction\n"; + return false; + } + + if (m_params) { free(m_params); m_params = nullptr; } + m_params = (icFloatNumber*)malloc(m_nParameters * sizeof(icFloatNumber)); + if (!m_params) return false; + for (int i = 0; i < m_nParameters; i++) m_params[i] = 0.0f; + + if (j.contains("parameters") && j["parameters"].is_array()) { + int cnt = std::min((int)m_nParameters, (int)j["parameters"].size()); + for (int i = 0; i < cnt; i++) + m_params[i] = (icFloatNumber)j["parameters"][i].get(); + } + return true; +} + +// =========================================================================== +// CIccMpeJsonToneMap +// =========================================================================== + +bool CIccMpeJsonToneMap::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + + // Luminance curve + if (m_pLumCurve) { + IccJson jLum; + if (!icToJsonCurve(jLum, m_pLumCurve)) + return false; + j["luminanceCurve"] = jLum; + } + + // Tone map functions (with duplicate detection) + IccJson funcs = IccJson::array(); + for (int i = 0; i < (int)m_nOutputChannels; i++) { + int dupIdx = -1; + for (int k = 0; k < i; k++) { + if (m_pToneFuncs[i] == m_pToneFuncs[k]) { dupIdx = k; break; } + } + + IccJson jFunc; + if (dupIdx >= 0) { + jFunc["type"] = "DuplicateFunction"; + jFunc["index"] = dupIdx; + } + else { + if (!m_pToneFuncs[i] || strcmp(m_pToneFuncs[i]->GetClassName(), "CIccJsonToneMapFunc") != 0) + return false; + if (!static_cast(m_pToneFuncs[i])->ToJson(jFunc)) + return false; + } + funcs.push_back(jFunc); + } + j["toneMapFunctions"] = funcs; + return true; +} + +bool CIccMpeJsonToneMap::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + + if (!nIn || !nOut || nIn != nOut + 1) { + parseStr += "Invalid inputChannels or outputChannels in ToneMapElement\n"; + return false; + } + m_nInputChannels = (icUInt16Number)nIn; + SetNumOutputChannels((icUInt16Number)nOut); + + if (!m_pToneFuncs) { + parseStr += "Unable to allocate ToneMapFunctions\n"; + return false; + } + + // Luminance curve + if (!j.contains("luminanceCurve") || !j["luminanceCurve"].is_object()) { + parseStr += "Missing luminanceCurve in ToneMapElement\n"; + return false; + } + icCurveSetCurvePtr pLumCurve = icFromJsonCurve(j["luminanceCurve"], parseStr); + if (!pLumCurve) { + parseStr += "Unable to parse luminanceCurve in ToneMapElement\n"; + return false; + } + SetLumCurve(pLumCurve); + + // Tone map functions + if (!j.contains("toneMapFunctions") || !j["toneMapFunctions"].is_array()) { + parseStr += "Missing toneMapFunctions in ToneMapElement\n"; + return false; + } + const IccJson &jFuncs = j["toneMapFunctions"]; + int nIndex = 0; + for (const IccJson &jFunc : jFuncs) { + if (nIndex >= nOut) { + parseStr += "Too many toneMapFunctions in ToneMapElement\n"; + return false; + } + + std::string type; + jGetString(jFunc, "type", type); + + if (type == "DuplicateFunction") { + int dupIdx = -1; + jGetValue(jFunc, "index", dupIdx); + if (dupIdx < 0 || dupIdx >= nIndex) { + parseStr += "Invalid index for DuplicateFunction in ToneMapElement\n"; + return false; + } + if (m_nFunc >= (icUInt16Number)nOut) { + parseStr += "Too many ToneMapFunctions\n"; + return false; + } + m_pToneFuncs[m_nFunc++] = m_pToneFuncs[dupIdx]; + } + else { + CIccJsonToneMapFunc *pFunc = new CIccJsonToneMapFunc(); + if (!pFunc->ParseJson(jFunc, parseStr)) { delete pFunc; return false; } + if (!Insert(pFunc)) { delete pFunc; return false; } + } + nIndex++; + } + + return (nIndex == nOut); +} + +// =========================================================================== +// CIccMpeJsonMatrix +// =========================================================================== + +bool CIccMpeJsonMatrix::ToJson(IccJson &j) +{ + if (m_pMatrix) { + IccJson matrix = IccJson::array(); + icUInt32Number nEntries = m_nInputChannels * m_nOutputChannels; + for (icUInt32Number i = 0; i < nEntries; i++) + matrix.push_back((double)m_pMatrix[i]); + j["matrix"] = matrix; + } + if (m_pConstants && m_bApplyConstants) { + IccJson constants = IccJson::array(); + for (icUInt16Number i = 0; i < m_nOutputChannels; i++) + constants.push_back((double)m_pConstants[i]); + j["constants"] = constants; + } + return true; +} + +bool CIccMpeJsonMatrix::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nInInt = 0, nOutInt = 0; + jGetValue(j, "inputChannels", nInInt); + jGetValue(j, "outputChannels", nOutInt); + icUInt16Number nIn = (icUInt16Number)nInInt; + icUInt16Number nOut = (icUInt16Number)nOutInt; + + bool bHasConstants = j.contains("constants") && j["constants"].is_array(); + if (!SetSize(nIn, nOut, bHasConstants)) return false; + + if (j.contains("matrix") && j["matrix"].is_array()) { + const IccJson &mat = j["matrix"]; + icUInt32Number nEntries = nIn * nOut; + for (icUInt32Number i = 0; i < nEntries && i < (icUInt32Number)mat.size(); i++) + m_pMatrix[i] = (icFloatNumber)mat[i].get(); + + bool bInvert = j.contains("invertMatrix") && j["invertMatrix"].is_boolean() + ? j["invertMatrix"].get() : false; + if (bInvert) { + if (nIn != nOut) { + parseStr += "Inversion of matrix requires square matrix\n"; + return false; + } + IIccMatrixInverter *pInverter = IccGetDefaultMatrixInverter(); + if (!pInverter || !pInverter->Invert(m_pMatrix, nOut, nIn)) { + parseStr += "Unable to invert matrix!\n"; + return false; + } + } + } + if (bHasConstants) { + const IccJson &con = j["constants"]; + for (icUInt16Number i = 0; i < nOut && i < (icUInt16Number)con.size(); i++) + m_pConstants[i] = (icFloatNumber)con[i].get(); + } + return true; +} + +// =========================================================================== +// CIccMpeJsonCLUT +// =========================================================================== + +bool CIccMpeJsonCLUT::ToJson(IccJson &j) +{ + if (m_pCLUT) + icCLUTToJson(j, m_pCLUT, icConvertFloat, true, "clut"); + return true; +} + +bool CIccMpeJsonCLUT::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in CLutElement\n"; + return false; + } + m_nInputChannels = (icUInt16Number)nIn; + m_nOutputChannels = (icUInt16Number)nOut; + + if (!j.contains("clut") || !j["clut"].is_object()) { + parseStr += "Missing clut data in CLutElement\n"; + return false; + } + + CIccCLUT *pCLUT = icCLUTFromJson(j["clut"], nIn, nOut, icConvertFloat, parseStr); + if (!pCLUT) return false; + + SetCLUT(pCLUT); + return m_pCLUT != nullptr; +} + +// =========================================================================== +// CIccMpeJsonExtCLUT +// =========================================================================== + +bool CIccMpeJsonExtCLUT::ToJson(IccJson &j) +{ + j["storageType"] = (int)m_storageType; + if (m_nReserved2) + j["reserved2"] = (int)m_nReserved2; + if (m_pCLUT) + icCLUTToJson(j, m_pCLUT, icConvertFloat, true, "clut"); + return true; +} + +bool CIccMpeJsonExtCLUT::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0, storageType = 0, reserved2 = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + jGetValue(j, "storageType", storageType); + jGetValue(j, "reserved2", reserved2); + + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in ExtCLutElement\n"; + return false; + } + m_nInputChannels = (icUInt16Number)nIn; + m_nOutputChannels = (icUInt16Number)nOut; + m_storageType = (icUInt16Number)storageType; + m_nReserved2 = (icUInt16Number)reserved2; + + if (!j.contains("clut") || !j["clut"].is_object()) { + parseStr += "Missing clut data in ExtCLutElement\n"; + return false; + } + + CIccCLUT *pCLUT = icCLUTFromJson(j["clut"], nIn, nOut, icConvertFloat, parseStr); + if (!pCLUT) return false; + + SetCLUT(pCLUT); + return m_pCLUT != nullptr; +} + +// =========================================================================== +// CIccMpeJsonBAcs / EAcs +// =========================================================================== + +bool CIccMpeJsonBAcs::ToJson(IccJson &j) +{ + char buf[32]; icGetSigStr(buf, sizeof(buf), m_signature); + j["signature"] = buf; + j["data"] = icJsonDumpHexData(m_pData, m_nDataSize); + return true; +} + +bool CIccMpeJsonBAcs::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string sig, hex; + jGetString(j, "signature", sig); + jGetString(j, "data", hex); + if (!sig.empty()) + m_signature = (icAcsSignature)icGetSigVal(sig.c_str()); + m_nDataSize = icJsonGetHexDataSize(hex.c_str()); + if (m_pData) { delete[] m_pData; m_pData = NULL; } + if (m_nDataSize) { + m_pData = new icUInt8Number[m_nDataSize]; + icJsonGetHexData(m_pData, hex.c_str(), m_nDataSize); + } + return true; +} + +bool CIccMpeJsonEAcs::ToJson(IccJson &j) +{ + char buf[32]; icGetSigStr(buf, sizeof(buf), m_signature); + j["signature"] = buf; + j["data"] = icJsonDumpHexData(m_pData, m_nDataSize); + return true; +} + +bool CIccMpeJsonEAcs::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string sig, hex; + jGetString(j, "signature", sig); + jGetString(j, "data", hex); + if (!sig.empty()) + m_signature = (icAcsSignature)icGetSigVal(sig.c_str()); + m_nDataSize = icJsonGetHexDataSize(hex.c_str()); + if (m_pData) { delete[] m_pData; m_pData = NULL; } + if (m_nDataSize) { + m_pData = new icUInt8Number[m_nDataSize]; + icJsonGetHexData(m_pData, hex.c_str(), m_nDataSize); + } + return true; +} + +// =========================================================================== +// CIccMpeJsonJabToXYZ / XYZToJab (shared CAM parameter helpers) +// =========================================================================== + +static bool icCAMParamsToJson(IccJson &j, CIccCamConverter *pCAM) +{ + if (!pCAM) + return false; + + IccJson jCAM; + icFloatNumber xyz[3]; + pCAM->GetParameter_WhitePoint(xyz); + jCAM["whitePoint"] = IccJson::array({ (double)xyz[0], (double)xyz[1], (double)xyz[2] }); + jCAM["luminance"] = (double)pCAM->GetParameter_La(); + jCAM["backgroundLuminance"] = (double)pCAM->GetParameter_Yb(); + jCAM["impactSurround"] = (double)pCAM->GetParameter_C(); + jCAM["chromaticInductionFactor"] = (double)pCAM->GetParameter_Nc(); + jCAM["adaptationFactor"] = (double)pCAM->GetParameter_F(); + j["colorAppearanceParams"] = jCAM; + return true; +} + +static bool icCAMParamsFromJson(const IccJson &j, std::string &parseStr, CIccCamConverter *pCAM) +{ + if (!j.contains("colorAppearanceParams") || !j["colorAppearanceParams"].is_object()) { + parseStr += "Missing colorAppearanceParams in Jab element\n"; + return false; + } + const IccJson &jCAM = j["colorAppearanceParams"]; + + if (!jCAM.contains("whitePoint") || !jCAM["whitePoint"].is_array() || jCAM["whitePoint"].size() < 3) { + parseStr += "Invalid whitePoint in colorAppearanceParams\n"; + return false; + } + icFloatNumber xyz[3]; + xyz[0] = (icFloatNumber)jCAM["whitePoint"][0].get(); + xyz[1] = (icFloatNumber)jCAM["whitePoint"][1].get(); + xyz[2] = (icFloatNumber)jCAM["whitePoint"][2].get(); + pCAM->SetParameter_WhitePoint(xyz); + + if (!jCAM.contains("luminance")) { + parseStr += "Missing luminance in colorAppearanceParams\n"; + return false; + } + pCAM->SetParameter_La((icFloatNumber)jCAM["luminance"].get()); + + if (!jCAM.contains("backgroundLuminance")) { + parseStr += "Missing backgroundLuminance in colorAppearanceParams\n"; + return false; + } + pCAM->SetParameter_Yb((icFloatNumber)jCAM["backgroundLuminance"].get()); + + if (!jCAM.contains("impactSurround")) { + parseStr += "Missing impactSurround in colorAppearanceParams\n"; + return false; + } + pCAM->SetParameter_C((icFloatNumber)jCAM["impactSurround"].get()); + + if (!jCAM.contains("chromaticInductionFactor")) { + parseStr += "Missing chromaticInductionFactor in colorAppearanceParams\n"; + return false; + } + pCAM->SetParameter_Nc((icFloatNumber)jCAM["chromaticInductionFactor"].get()); + + if (!jCAM.contains("adaptationFactor")) { + parseStr += "Missing adaptationFactor in colorAppearanceParams\n"; + return false; + } + pCAM->SetParameter_F((icFloatNumber)jCAM["adaptationFactor"].get()); + + return true; +} + +bool CIccMpeJsonJabToXYZ::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + return icCAMParamsToJson(j, m_pCAM); +} + +bool CIccMpeJsonJabToXYZ::ParseJson(const IccJson &j, std::string &parseStr) +{ + m_nInputChannels = m_nOutputChannels = 3; + + CIccCamConverter *pCAM = new CIccCamConverter(); + if (!icCAMParamsFromJson(j, parseStr, pCAM)) { + delete pCAM; + return false; + } + SetCAM(pCAM); + return true; +} + +bool CIccMpeJsonXYZToJab::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + return icCAMParamsToJson(j, m_pCAM); +} + +bool CIccMpeJsonXYZToJab::ParseJson(const IccJson &j, std::string &parseStr) +{ + m_nInputChannels = m_nOutputChannels = 3; + + CIccCamConverter *pCAM = new CIccCamConverter(); + if (!icCAMParamsFromJson(j, parseStr, pCAM)) { + delete pCAM; + return false; + } + SetCAM(pCAM); + return true; +} + +// =========================================================================== +// CIccMpeJsonCalculator +// =========================================================================== + +void CIccMpeJsonCalculator::clean() +{ + m_sImport = "*"; + m_declVarMap.clear(); + m_varMap.clear(); + m_macroMap.clear(); + m_macroLocalMap.clear(); + + for (auto &mp : m_mpeList) { + if (mp.m_ptr) { delete mp.m_ptr; mp.m_ptr = nullptr; } + } + m_mpeList.clear(); + + for (auto &kv : m_mpeMap) { + if (kv.second.m_ptr) { delete kv.second.m_ptr; kv.second.m_ptr = nullptr; } + } + m_mpeMap.clear(); + + m_nNextVar = 0; + m_nNextMpe = 0; +} + +bool CIccMpeJsonCalculator::validNameChar(char c, bool bFirst) +{ + if (bFirst && !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')) + return false; + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '_')) + return false; + return true; +} + +bool CIccMpeJsonCalculator::validName(const char *szName) +{ + if (!szName || !*szName) + return false; + for (const char *p = szName; *p; p++) { + if (!validNameChar(*p, p == szName)) + return false; + } + return true; +} + +bool CIccMpeJsonCalculator::ParseChanMap(ChanVarMap &chanMap, const char *szNames, int nChannels) +{ + chanMap.clear(); + if (!szNames) + return false; + + int i = 0, size = 1; + std::string name; + IndexSizePair isp; + + for (const char *p = szNames; *p && i < nChannels; p++) { + bool bFirst = name.empty(); + if (*p == ' ') { + if (!bFirst) { + isp.first = i; isp.second = size; + chanMap[name] = isp; + name.clear(); i += size; size = 1; + } + } + else if (*p == '[' || *p == '(') { + size = atoi(p + 1); + if (size < 0 || i + size > nChannels) return false; + for (; *p && *p != ']' && *p != ')'; p++); + if (!*p) p--; + } + else if (validNameChar(*p, bFirst)) { + name += *p; + } + else { + return false; + } + } + if (!name.empty() && i < nChannels) { + isp.first = i; isp.second = size; + chanMap[name] = isp; + } + return true; +} + +bool CIccMpeJsonCalculator::ValidMacroCalls(const char *szMacroText, + std::string macroStack, + std::string &parseStr) const +{ + // Macros can be called as call{name} or #name + auto FindNextMacro = [](const char *text) -> const char* { + const char *a = strstr(text, "call{"); + const char *b = strchr(text, '#'); + if (b && (!a || b < a)) return b; + return a; + }; + + for (const char *ptr = FindNextMacro(szMacroText); ptr; ptr = FindNextMacro(ptr)) { + bool isHash = (ptr[0] == '#'); + if (isHash) ptr++; + CIccFuncTokenizer parse(ptr, true); + parse.GetNext(); + std::string name = isHash ? parse.GetName() : parse.GetReference(); + + MacroMap::const_iterator m = m_macroMap.find(name); + if (m == m_macroMap.end()) { + parseStr += "Call to undefined macro '" + name + "'\n"; + return false; + } + std::string sm = "*" + name + "*"; + if (strstr(macroStack.c_str(), sm.c_str())) { + parseStr += "Macro recursion detected in call to '" + name + "'\n"; + return false; + } + if (!ValidMacroCalls(m->second.c_str(), macroStack + name + "*", parseStr)) + return false; + ptr++; + } + return true; +} + +bool CIccMpeJsonCalculator::ValidateMacroCalls(std::string &parseStr) const +{ + for (auto &kv : m_macroMap) { + if (!ValidMacroCalls(kv.second.c_str(), std::string("*") + kv.first + "*", parseStr)) + return false; + } + return true; +} + +bool CIccMpeJsonCalculator::Flatten(std::string &flatStr, std::string macroName, + const char *szFunc, std::string &parseStr, + icUInt32Number nLocalsOffset) +{ + auto FindNextMacro = [](const char *text) -> const char* { + const char *a = strstr(text, "call{"); + const char *b = strchr(text, '#'); + if (b && (!a || b < a)) return b; + return a; + }; + + CIccFuncTokenizer parse(szFunc, true); + + while (parse.GetNext()) { + std::string token = parse.GetLast(); + const char *tok = token.c_str(); + + // ── Macro call ──────────────────────────────────────────────────────── + if (!strncmp(tok, "call{", 5) || tok[0] == '#') { + std::string nameIter, name; + if (tok[0] == '#') + nameIter = token.substr(1); + else + nameIter = parse.GetReference(); + + for (const char *p = nameIter.c_str(); *p && *p != '[' && *p != '('; p++) + name += *p; + + MacroMap::iterator m = m_macroMap.find(name); + if (m == m_macroMap.end()) { + parseStr += "Call to undefined macro '" + name + "'\n"; + return false; + } + icUInt16Number nLocalsSize = 0; + TempDeclVarMap::iterator locals = m_macroLocalMap.find(macroName); + if (locals != m_macroLocalMap.end()) + nLocalsSize = locals->second.m_size; + + if (!Flatten(flatStr, name, m->second.c_str(), parseStr, nLocalsOffset + nLocalsSize)) + return false; + } + + // ── Input/output channel references ─────────────────────────────────── + else if (!strncmp(tok, "in{", 3) || !strncmp(tok, "out{", 4)) { + std::string op = parse.GetName(); + std::string ref = parse.GetReference(); + std::string refroot; + for (const char *p = ref.c_str(); *p && *p != ',' && *p != '(' && *p != '['; p++) + refroot += *p; + + ChanVarMap *pMap = (op == "in") ? &m_inputMap : &m_outputMap; + ChanVarMap::iterator ci = pMap->find(refroot); + if (ci == pMap->end()) { + parseStr += "Invalid '" + op + "' channel reference '" + refroot + "'\n"; + return false; + } + + char index[80]; + if (refroot != ref) { + std::string select = ref.substr(refroot.size()); + int offset = 0, sz = 1; + if (select[0] == '[' || select[0] == '(') { + const char *p; + offset = atoi(select.c_str() + 1); + for (p = select.c_str() + 1; *p && *p != ')' && *p != ']'; p++); + select = p + 1; + } + if (select[0] == ',') sz = atoi(select.c_str() + 1); + if (sz < 0 || offset < 0 || + ci->second.first + offset + sz > (int)m_nInputChannels) { + parseStr += "Invalid '" + op + "' channel offset/size '" + refroot + "'\n"; + return false; + } + snprintf(index, sizeof(index), "(%d,%d)", ci->second.first + offset, sz); + } + else if (ci->second.second > 1) { + snprintf(index, sizeof(index), "(%d,%d)", ci->second.first, ci->second.second); + } + else { + snprintf(index, sizeof(index), "(%d)", ci->second.first); + } + flatStr += op + index + " "; + } + + // ── Temporary variable operations ───────────────────────────────────── + else if (!strncmp(tok, "tget{", 5) || !strncmp(tok, "tput{", 5) || + !strncmp(tok, "tsav{", 5)) { + std::string op = parse.GetName(); + std::string ref = parse.GetReference(); + std::string refroot; + for (const char *p = ref.c_str(); *p && *p != '[' && *p != '('; p++) + refroot += *p; + + // Macro local variables (prefixed with @) + if (!macroName.empty() && !refroot.empty() && refroot[0] == '@') { + std::string localName = refroot.substr(1); + TempDeclVarMap::iterator locals = m_macroLocalMap.find(macroName); + if (locals == m_macroLocalMap.end()) { + parseStr += "Reference to undeclared local '" + localName + "' in macro '" + macroName + "'\n"; + return false; + } + bool localFound = false; + unsigned long nLocalOffset = 0, nLocalSize = 0; + for (auto &m : locals->second.m_members) { + if (m.m_name == localName) { + nLocalOffset = m.m_pos; nLocalSize = m.m_size; localFound = true; break; + } + } + if (!localFound) { + parseStr += "Reference to undeclared local '" + localName + "' in macro '" + macroName + "'\n"; + return false; + } + int voffset = 0, vsize = (int)nLocalSize; + if (ref != refroot) { + CIccFuncTokenizer p2(ref.c_str()); + p2.GetNext(); + icUInt16Number vo = 0, vs = 1; + p2.GetIndex(vo, vs, 0, 1); + voffset = vo; vsize = vs + 1; + } + if (voffset + vsize > (int)nLocalSize) { + parseStr += "Out of bounds indexing local '" + refroot + "' in macro '" + macroName + "'\n"; + return false; + } + if (nLocalsOffset + nLocalOffset + voffset + vsize > 65536) { + parseStr += "Temporary variable addressing out of bounds\n"; + return false; + } + char idx[80]; + if (vsize == 1) + snprintf(idx, sizeof(idx), "[%lu]", nLocalsOffset + nLocalOffset + voffset); + else + snprintf(idx, sizeof(idx), "[%lu,%d]", nLocalsOffset + nLocalOffset + voffset, vsize); + flatStr += "l" + op.substr(1) + idx + " "; + } + else { + // Named temporary variable + TempVarMap::iterator var = m_varMap.find(refroot); + if (var == m_varMap.end()) { + // Try to allocate from declVarMap + std::string root; + for (const char *p = refroot.c_str(); *p && *p != '.'; p++) root += *p; + + TempDeclVarMap::iterator decl = m_declVarMap.find(root); + if (decl == m_declVarMap.end()) { + parseStr += "Reference to undeclared variable '" + ref + "'\n"; + return false; + } + if (decl->second.m_pos < 0) { + m_varMap[root] = CIccTempVar(root, m_nNextVar, decl->second.m_size); + decl->second.m_pos = m_nNextVar; + if (refroot.find('.') != std::string::npos) { + for (auto &m : decl->second.m_members) { + m_varMap[root + "." + m.m_name] = + CIccTempVar(root + "." + m.m_name, m_nNextVar + m.m_pos, m.m_size); + } + } + if (m_nNextVar + decl->second.m_size > 65536) { + parseStr += "Temporary variable addressing out of bounds\n"; + return false; + } + m_nNextVar += decl->second.m_size; + } + else { + m_varMap[root] = CIccTempVar(root, decl->second.m_pos, decl->second.m_size); + if (refroot.find('.') != std::string::npos) { + for (auto &m : decl->second.m_members) { + m_varMap[root + "." + m.m_name] = + CIccTempVar(root + "." + m.m_name, decl->second.m_pos + m.m_pos, m.m_size); + } + } + } + var = m_varMap.find(refroot); + if (var == m_varMap.end()) { + parseStr += "Reference to undeclared variable '" + refroot + "'\n"; + return false; + } + } + int voffset = 0, vsize = var->second.m_size; + if (ref != refroot) { + CIccFuncTokenizer p2(ref.c_str()); + p2.GetNext(); + icUInt16Number vo = 0, vs = 1; + p2.GetIndex(vo, vs, 0, 1); + voffset = vo; vsize = vs + 1; + } + if (voffset + vsize > var->second.m_size) { + parseStr += "Out of bounds indexing '" + refroot + "'\n"; + return false; + } + if (var->second.m_pos + voffset + vsize > 65536) { + parseStr += "Temporary variable addressing out of bounds\n"; + return false; + } + char idx[80]; + if (vsize == 1) + snprintf(idx, sizeof(idx), "[%d]", var->second.m_pos + voffset); + else + snprintf(idx, sizeof(idx), "[%d,%d]", var->second.m_pos + voffset, vsize); + flatStr += op + idx + " "; + } + } + + // ── Sub-element references ───────────────────────────────────────────── + else if (!strncmp(tok, "elem{", 5) || !strncmp(tok, "curv{", 5) || + !strncmp(tok, "clut{", 5) || !strncmp(tok, "mtx{", 4) || + !strncmp(tok, "fJab{", 5) || !strncmp(tok, "tJab{", 5) || + !strncmp(tok, "calc{", 5) || !strncmp(tok, "tint{", 5)) { + std::string op = parse.GetName(); + std::string ref = parse.GetReference(); + MpePtrMap::iterator e = m_mpeMap.find(ref); + if (e == m_mpeMap.end()) { + parseStr += "Unknown sub-element reference: " + token + "\n"; + return false; + } + if (e->second.m_nIndex < 0) { + if (e->second.m_ptr) { + m_mpeList.push_back(CIccMpePtr(e->second.m_ptr, m_nNextMpe)); + e->second.m_ptr = nullptr; + e->second.m_nIndex = m_nNextMpe; + m_nNextMpe++; + } + else { + parseStr += "Invalid sub-element reference: " + token + "\n"; + return false; + } + } + char idx[80]; + snprintf(idx, sizeof(idx), "(%d)", e->second.m_nIndex); + flatStr += op + idx + " "; + } + + // ── Pass through any other token ────────────────────────────────────── + else { + flatStr += tok; + flatStr += " "; + } + } + return true; +} + +bool CIccMpeJsonCalculator::UpdateLocals(std::string &func, std::string sFunc, + std::string &parseStr, int nLocalsOffset) +{ + func.clear(); + CIccFuncTokenizer parse(sFunc.c_str(), true); + + while (parse.GetNext()) { + std::string token = parse.GetLast(); + const char *tok = token.c_str(); + + if (!strncmp(tok, "lget[", 5) || !strncmp(tok, "lput[", 5) || + !strncmp(tok, "lsav[", 5)) { + std::string op = parse.GetName(); + CIccFuncTokenizer p2(tok + 4); + icUInt16Number vo = 0, vs = 1; + p2.GetIndex(vo, vs, 0, 1); + int voffset = vo + nLocalsOffset; + int vsize = vs + 1; + if (voffset + vsize > 65535) { + parseStr += "Local variable out of bounds - too many variables.\n"; + return false; + } + char idx[80]; + if (vsize == 1) snprintf(idx, sizeof(idx), "[%d]", voffset); + else snprintf(idx, sizeof(idx), "[%d,%d]", voffset, vsize); + func += "t" + op.substr(1) + idx + " "; + } + else { + func += tok; + func += " "; + } + } + return true; +} + +// --------------------------------------------------------------------------- +// ParseImport – recursively load variables, macros, and sub-elements from +// either the top-level JSON (importPath=="*") or an imported JSON file. +// --------------------------------------------------------------------------- +bool CIccMpeJsonCalculator::ParseImport(const IccJson &j, std::string importPath, + std::string &parseStr) +{ + // Process imports (nested JSON files) + if (j.contains("imports") && j["imports"].is_array()) { + for (const IccJson &imp : j["imports"]) { + std::string file; + jGetString(imp, "file", file); + if (file.empty()) { + parseStr += "Missing file in import entry\n"; + return false; + } + std::string look = "*" + file + "*"; + if (strstr(importPath.c_str(), look.c_str())) + continue; // Already imported – skip + + std::ifstream ifs(file); + if (!ifs.is_open()) { + parseStr += "Unable to open import file '" + file + "'\n"; + return false; + } + IccJson jImport; + try { + ifs >> jImport; + } + catch (...) { + parseStr += "Invalid JSON in import file '" + file + "'\n"; + return false; + } + + if (!ParseImport(jImport, importPath + file + "*", parseStr)) + return false; + } + } + + // Process variable declarations + if (j.contains("variables") && j["variables"].is_array()) { + for (const IccJson &jVar : j["variables"]) { + std::string name; + jGetString(jVar, "name", name); + if (!validName(name.c_str())) { + parseStr += "Invalid variable name '" + name + "'\n"; + return false; + } + if (m_declVarMap.find(name) != m_declVarMap.end()) { + parseStr += "Variable '" + name + "' was previously declared\n"; + return false; + } + + int offset = -1, size = 1; + jGetValue(jVar, "position", offset); + jGetValue(jVar, "size", size); + if (size < 1) size = 1; + + if (offset >= 0 && importPath != "*") { + parseStr += "Position cannot be specified for imported variables\n"; + return false; + } + + CIccTempDeclVar var(name, offset, (icUInt16Number)size); + + // Optional members string: "m1 m2[2] m3" + std::string members; + jGetString(jVar, "members", members); + if (!members.empty()) { + CIccFuncTokenizer parse(members.c_str()); + int off = 0; + while (parse.GetNext()) { + std::string mname = parse.GetName(); + if (!validName(mname.c_str())) { + parseStr += "Invalid member name '" + mname + "' for variable '" + name + "'\n"; + return false; + } + icUInt16Number msize = 0, extra = 0; + parse.GetIndex(msize, extra, 1, 0); + msize++; + if (msize < 1) msize = 1; + var.m_members.push_back(CIccTempVar(mname, off, msize)); + off += msize; + } + if (var.m_size < off) var.m_size = off; + } + m_declVarMap[name] = var; + } + } + + // Process macro definitions + if (j.contains("macros") && j["macros"].is_array()) { + for (const IccJson &jMacro : j["macros"]) { + std::string name, body; + jGetString(jMacro, "name", name); + jGetString(jMacro, "body", body); + + if (!validName(name.c_str())) { + parseStr += "Invalid macro name '" + name + "'\n"; + return false; + } + if (body.empty()) { + parseStr += "Missing body for macro '" + name + "'\n"; + return false; + } + + MacroMap::iterator m = m_macroMap.find(name); + if (m != m_macroMap.end()) { + if (m->second == body) continue; + parseStr += "Macro '" + name + "' was previously defined differently\n"; + return false; + } + m_macroMap[name] = body; + + // Optional local variable declarations: "loc1 loc2[2]" + std::string locals; + jGetString(jMacro, "locals", locals); + if (!locals.empty()) { + CIccTempDeclVar var(name, 0, 0); + CIccFuncTokenizer parse(locals.c_str()); + int off = 0; + while (parse.GetNext()) { + std::string lname = parse.GetName(); + if (!validName(lname.c_str())) { + parseStr += "Invalid local name '" + lname + "' for macro '" + name + "'\n"; + return false; + } + icUInt16Number lsize = 0, extra = 0; + parse.GetIndex(lsize, extra, 1, 0); + lsize++; + if (lsize < 1) lsize = 1; + var.m_members.push_back(CIccTempVar(lname, off, lsize)); + off += lsize; + } + if (var.m_size < off) var.m_size = off; + m_macroLocalMap[name] = var; + } + } + } + + // Process sub-elements + if (j.contains("subElements") && j["subElements"].is_array()) { + for (const IccJson &jElem : j["subElements"]) { + std::string name; + jGetString(jElem, "name", name); + + if (!name.empty()) { + if (!validName(name.c_str())) { + parseStr += "Invalid SubElement name '" + name + "'\n"; + return false; + } + if (m_mpeMap.find(name) != m_mpeMap.end()) { + parseStr += "Duplicate SubElement '" + name + "'\n"; + return false; + } + } + else if (importPath != "*") { + parseStr += "Imported SubElements must be named\n"; + return false; + } + + // Read the "type" discriminator field + std::string typeName; + jGetString(jElem, "type", typeName); + if (typeName.empty()) { + parseStr += "Missing 'type' field in subElements entry\n"; + return false; + } + + icElemTypeSignature sig = icJsonGetElemTypeNameSig(typeName.c_str()); + CIccMultiProcessElement *pMpe = CIccMpeCreator::CreateElement(sig); + if (!pMpe) { + parseStr += "Unknown SubElement type '" + typeName + "'\n"; + return false; + } + + // Propagate import path to nested calculators + if (!strcmp(pMpe->GetClassName(), "CIccMpeJsonCalculator")) { + static_cast(pMpe)->m_sImport = importPath; + } + + IIccExtensionMpe *pExt = pMpe->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccMpeJson") != 0) { + parseStr += std::string(pMpe->GetClassName()) + " is not a CIccMpeJson\n"; + delete pMpe; + return false; + } + + CIccMpeJson *pJsonMpe = static_cast(pExt); + if (!pJsonMpe->ParseJson(jElem, parseStr)) { + parseStr += "Unable to parse SubElement '" + typeName + "'\n"; + delete pMpe; + return false; + } + + if (name.empty()) { + m_mpeList.push_back(CIccMpePtr(pMpe, m_nNextMpe)); + m_nNextMpe++; + } + else { + m_mpeMap[name] = CIccMpePtr(pMpe); + } + } + } + + return true; +} + +bool CIccMpeJsonCalculator::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)NumInputChannels(); + j["outputChannels"] = (int)NumOutputChannels(); + + // Emit sub-elements (anonymous, in order) + if (m_SubElem && m_nSubElem) { + IccJson elems = IccJson::array(); + for (icUInt32Number i = 0; i < m_nSubElem; i++) { + if (!m_SubElem[i]) return false; + IIccExtensionMpe *pExt = m_SubElem[i]->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccMpeJson") != 0) + return false; + CIccMpeJson *pJsonMpe = static_cast(pExt); + IccJson elemObj; + elemObj["type"] = icJsonGetElemTypeName(m_SubElem[i]->GetType()); + elemObj["inputChannels"] = (int)m_SubElem[i]->NumInputChannels(); + elemObj["outputChannels"] = (int)m_SubElem[i]->NumOutputChannels(); + if (m_SubElem[i]->m_nReserved) + elemObj["Reserved"] = (int)m_SubElem[i]->m_nReserved; + pJsonMpe->ToJson(elemObj); + elems.push_back(elemObj); + } + j["subElements"] = elems; + } + + // Emit main function bytecode as text + if (m_calcFunc) { + std::string desc; + m_calcFunc->Describe(desc, 100, 0); + j["mainFunction"] = desc; + } + + return true; +} + +bool CIccMpeJsonCalculator::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in CalculatorElement\n"; + return false; + } + + SetSize((icUInt16Number)nIn, (icUInt16Number)nOut); + clean(); + + // Named input/output channels + std::string inputNames, outputNames; + jGetString(j, "inputNames", inputNames); + jGetString(j, "outputNames", outputNames); + + if (!ParseChanMap(m_inputMap, inputNames.c_str(), m_nInputChannels)) { + parseStr += "Invalid name for inputChannels\n"; + return false; + } + if (!ParseChanMap(m_outputMap, outputNames.c_str(), m_nOutputChannels)) { + parseStr += "Invalid name for outputChannels\n"; + return false; + } + + // Load variables, macros, and sub-elements (including any imports) + if (!ParseImport(j, "*", parseStr)) + return false; + + if (!ValidateMacroCalls(parseStr)) + return false; + + // Main function string + std::string mainFunc; + jGetString(j, "mainFunction", mainFunc); + if (mainFunc.empty()) { + parseStr += "Missing mainFunction in CalculatorElement\n"; + return false; + } + + // Advance m_nNextVar past any fixed-position variables + for (auto &kv : m_declVarMap) { + if (kv.second.m_pos >= 0) { + int next = kv.second.m_pos + kv.second.m_size; + if (next > m_nNextVar) m_nNextVar = next; + } + } + + // Expand macros, resolve variable / channel / element references + std::string flatFunc; + if (!Flatten(flatFunc, "", mainFunc.c_str(), parseStr, 0)) + return false; + + // Resolve lget/lput/lsav local variable offsets if macros used locals + if (!m_macroLocalMap.empty()) { + std::string localFunc; + if (!UpdateLocals(localFunc, flatFunc, parseStr, m_nNextVar)) + return false; + flatFunc = localFunc; + } + + // Transfer sub-elements referenced from the function to the compiled array + int n = 0; + for (auto &mp : m_mpeList) { + SetSubElem(n++, mp.m_ptr); + mp.m_ptr = nullptr; + } + + icFuncParseStatus stat = SetCalcFunc(flatFunc.c_str(), parseStr); + if (stat != icFuncParseNoError) { + char buf[65]; + int len = std::min(64, (int)flatFunc.size()); + strncpy(buf, flatFunc.c_str(), len); + buf[len] = 0; + switch (stat) { + case icFuncParseSyntaxError: + parseStr += "Syntax error in CalculatorElement MainFunction: \""; break; + case icFuncParseInvalidOperation: + parseStr += "Invalid operation in CalculatorElement MainFunction: \""; break; + case icFuncParseStackUnderflow: + parseStr += "Stack underflow in CalculatorElement MainFunction: \""; break; + case icFuncParseInvalidChannel: + parseStr += "Invalid channel in CalculatorElement MainFunction: \""; break; + default: + parseStr += "Unable to parse CalculatorElement MainFunction: \""; + } + parseStr += buf; + parseStr += "\"\n"; + return false; + } + + clean(); // Free all parse-time state; compiled data is in base class + return true; +} + +// =========================================================================== +// Spectral elements – shared helpers +// =========================================================================== + +static IccJson icSpectralRangeToJson(const icSpectralRange &range) +{ + IccJson j; + j["start"] = (double)icF16toF(range.start); + j["end"] = (double)icF16toF(range.end); + j["steps"] = (int)range.steps; + return j; +} + +static bool icSpectralRangeFromJson(const IccJson &j, icSpectralRange &range, std::string &parseStr) +{ + if (!j.contains("wavelengths") || !j["wavelengths"].is_object()) { + parseStr += "Missing wavelengths in spectral element\n"; + return false; + } + const IccJson &jw = j["wavelengths"]; + double dStart = 0.0, dEnd = 0.0; + int nSteps = 0; + jGetValue(jw, "start", dStart); + jGetValue(jw, "end", dEnd); + jGetValue(jw, "steps", nSteps); + if (!nSteps) { + parseStr += "Invalid spectral range (steps==0)\n"; + return false; + } + range.start = icFtoF16((icFloat32Number)dStart); + range.end = icFtoF16((icFloat32Number)dEnd); + range.steps = (icUInt16Number)nSteps; + return true; +} + +static IccJson icFloatArrayToJson(const icFloatNumber *buf, int n) +{ + IccJson arr = IccJson::array(); + for (int i = 0; i < n; i++) + arr.push_back((double)buf[i]); + return arr; +} + +static bool icFloatArrayFromJson(const IccJson &j, const char *field, + icFloatNumber *buf, int n, std::string &parseStr, + bool bRequired = true) +{ + if (!j.contains(field) || !j[field].is_array()) { + if (bRequired) + parseStr += std::string("Missing ") + field + " in spectral element\n"; + return !bRequired; + } + const IccJson &arr = j[field]; + int cnt = std::min(n, (int)arr.size()); + for (int i = 0; i < cnt; i++) + buf[i] = (icFloatNumber)arr[i].get(); + return true; +} + +// =========================================================================== +// CIccMpeJsonEmissionMatrix / CIccMpeJsonInvEmissionMatrix +// =========================================================================== + +// numVectors() is protected — helpers take it as an explicit parameter +static bool icSpectralMatrixToJson(IccJson &j, CIccMpeSpectralMatrix *pMtx, int nVectors) +{ + j["inputChannels"] = (int)pMtx->NumInputChannels(); + j["outputChannels"] = (int)pMtx->NumOutputChannels(); + j["wavelengths"] = icSpectralRangeToJson(pMtx->GetRange()); + + if (pMtx->GetWhite()) + j["whiteData"] = icFloatArrayToJson(pMtx->GetWhite(), pMtx->GetRange().steps); + if (pMtx->GetMatrix()) + j["matrixData"] = icFloatArrayToJson(pMtx->GetMatrix(), nVectors * pMtx->GetRange().steps); + if (pMtx->GetOffset()) + j["offsetData"] = icFloatArrayToJson(pMtx->GetOffset(), pMtx->GetRange().steps); + return true; +} + +static bool icSpectralMatrixFromJson(const IccJson &j, CIccMpeSpectralMatrix *pMtx, + int nVectors, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in spectral matrix element\n"; + return false; + } + + icSpectralRange range{}; + if (!icSpectralRangeFromJson(j, range, parseStr)) + return false; + + if (!pMtx->SetSize((icUInt16Number)nIn, (icUInt16Number)nOut, range)) { + parseStr += "Unable to SetSize in spectral matrix element\n"; + return false; + } + + if (!icFloatArrayFromJson(j, "whiteData", pMtx->GetWhite(), range.steps, parseStr)) + return false; + if (!icFloatArrayFromJson(j, "matrixData", pMtx->GetMatrix(), nVectors * range.steps, parseStr)) + return false; + // offsetData optional — zero-fill if absent + if (j.contains("offsetData") && j["offsetData"].is_array()) { + icFloatArrayFromJson(j, "offsetData", pMtx->GetOffset(), range.steps, parseStr, false); + } + else { + memset(pMtx->GetOffset(), 0, range.steps * sizeof(icFloatNumber)); + } + return true; +} + +bool CIccMpeJsonEmissionMatrix::ToJson(IccJson &j) +{ + return icSpectralMatrixToJson(j, this, numVectors()); +} + +bool CIccMpeJsonEmissionMatrix::ParseJson(const IccJson &j, std::string &parseStr) +{ + // For EmissionMatrix numVectors() == m_nInputChannels; parse nIn first then pass it + int nIn = 0; + jGetValue(j, "inputChannels", nIn); + return icSpectralMatrixFromJson(j, this, nIn, parseStr); +} + +bool CIccMpeJsonInvEmissionMatrix::ToJson(IccJson &j) +{ + return icSpectralMatrixToJson(j, this, numVectors()); +} + +bool CIccMpeJsonInvEmissionMatrix::ParseJson(const IccJson &j, std::string &parseStr) +{ + // For InvEmissionMatrix numVectors() == m_nOutputChannels; parse nOut first then pass it + int nOut = 0; + jGetValue(j, "outputChannels", nOut); + return icSpectralMatrixFromJson(j, this, nOut, parseStr); +} + +// =========================================================================== +// CIccMpeJsonEmissionCLUT / CIccMpeJsonReflectanceCLUT +// (m_flags, m_nStorageType, m_Range are protected — accessed from derived member functions) +// =========================================================================== + +// Shared parse helper: fills out-params; caller owns pWhite and pCLUT on success. +static bool icSpectralCLUTParseJson(const IccJson &j, std::string &parseStr, + int &nIn, int &nOut, + icUInt32Number &flags, icUInt16Number &storageType, + icSpectralRange &range, + icFloatNumber *&pWhite, CIccCLUT *&pCLUT) +{ + int iFlags = 0, iStorageType = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + jGetValue(j, "flags", iFlags); + jGetValue(j, "storageType", iStorageType); + flags = (icUInt32Number)iFlags; + storageType = (icUInt16Number)iStorageType; + + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in spectral CLUT element\n"; + return false; + } + + if (!icSpectralRangeFromJson(j, range, parseStr)) + return false; + + pWhite = (icFloatNumber*)malloc(range.steps * sizeof(icFloatNumber)); + if (!pWhite) { + parseStr += "White buffer memory error in spectral CLUT element\n"; + return false; + } + if (!icFloatArrayFromJson(j, "whiteData", pWhite, range.steps, parseStr)) { + free(pWhite); pWhite = nullptr; + return false; + } + + if (!j.contains("clut") || !j["clut"].is_object()) { + parseStr += "Missing clut data in spectral CLUT element\n"; + free(pWhite); pWhite = nullptr; + return false; + } + + // CLUT output channels = spectral steps, not the element's final output channels + pCLUT = icCLUTFromJson(j["clut"], nIn, range.steps, icConvertFloat, parseStr); + if (!pCLUT) { + free(pWhite); pWhite = nullptr; + return false; + } + return true; +} + +bool CIccMpeJsonEmissionCLUT::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + j["flags"] = (int)m_flags; + j["storageType"] = (int)m_nStorageType; + j["wavelengths"] = icSpectralRangeToJson(m_Range); + if (m_pWhite) + j["whiteData"] = icFloatArrayToJson(m_pWhite, m_Range.steps); + if (m_pCLUT) + icCLUTToJson(j, m_pCLUT, icConvertFloat, true, "clut"); + return true; +} + +bool CIccMpeJsonEmissionCLUT::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + icUInt32Number flags = 0; + icUInt16Number storageType = 0; + icSpectralRange range{}; + icFloatNumber *pWhite = nullptr; + CIccCLUT *pCLUT = nullptr; + + if (!icSpectralCLUTParseJson(j, parseStr, nIn, nOut, flags, storageType, range, pWhite, pCLUT)) + return false; + + SetData(pCLUT, storageType, range, pWhite, (icUInt16Number)nOut); + m_flags = flags; + return true; +} + +bool CIccMpeJsonReflectanceCLUT::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + j["flags"] = (int)m_flags; + j["storageType"] = (int)m_nStorageType; + j["wavelengths"] = icSpectralRangeToJson(m_Range); + if (m_pWhite) + j["whiteData"] = icFloatArrayToJson(m_pWhite, m_Range.steps); + if (m_pCLUT) + icCLUTToJson(j, m_pCLUT, icConvertFloat, true, "clut"); + return true; +} + +bool CIccMpeJsonReflectanceCLUT::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + icUInt32Number flags = 0; + icUInt16Number storageType = 0; + icSpectralRange range{}; + icFloatNumber *pWhite = nullptr; + CIccCLUT *pCLUT = nullptr; + + if (!icSpectralCLUTParseJson(j, parseStr, nIn, nOut, flags, storageType, range, pWhite, pCLUT)) + return false; + + SetData(pCLUT, storageType, range, pWhite, (icUInt16Number)nOut); + m_flags = flags; + return true; +} + +// =========================================================================== +// CIccMpeJsonEmissionObserver / CIccMpeJsonReflectanceObserver +// (m_flags and m_Range are protected — accessed from derived member functions) +// =========================================================================== + +bool CIccMpeJsonEmissionObserver::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + j["flags"] = (int)m_flags; + j["wavelengths"] = icSpectralRangeToJson(m_Range); + if (m_pWhite) + j["whiteData"] = icFloatArrayToJson(m_pWhite, m_Range.steps); + return true; +} + +bool CIccMpeJsonEmissionObserver::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0, flags = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + jGetValue(j, "flags", flags); + m_flags = (icUInt16Number)flags; + + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in EmissionObserverElement\n"; + return false; + } + + icSpectralRange range{}; + if (!icSpectralRangeFromJson(j, range, parseStr)) + return false; + + if (range.steps != (icUInt16Number)nIn) { + parseStr += "Spectral observer wavelength steps must equal inputChannels\n"; + return false; + } + + if (!SetSize((icUInt16Number)nIn, (icUInt16Number)nOut, range)) { + parseStr += "Unable to SetSize in EmissionObserverElement\n"; + return false; + } + + return icFloatArrayFromJson(j, "whiteData", m_pWhite, range.steps, parseStr); +} + +bool CIccMpeJsonReflectanceObserver::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + j["flags"] = (int)m_flags; + j["wavelengths"] = icSpectralRangeToJson(m_Range); + if (m_pWhite) + j["whiteData"] = icFloatArrayToJson(m_pWhite, m_Range.steps); + return true; +} + +bool CIccMpeJsonReflectanceObserver::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0, flags = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + jGetValue(j, "flags", flags); + m_flags = (icUInt16Number)flags; + + if (!nIn || !nOut) { + parseStr += "Invalid inputChannels or outputChannels in ReflectanceObserverElement\n"; + return false; + } + + icSpectralRange range{}; + if (!icSpectralRangeFromJson(j, range, parseStr)) + return false; + + if (range.steps != (icUInt16Number)nIn) { + parseStr += "Spectral observer wavelength steps must equal inputChannels\n"; + return false; + } + + if (!SetSize((icUInt16Number)nIn, (icUInt16Number)nOut, range)) { + parseStr += "Unable to SetSize in ReflectanceObserverElement\n"; + return false; + } + + return icFloatArrayFromJson(j, "whiteData", m_pWhite, range.steps, parseStr); +} + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif diff --git a/IccJSON/IccLibJSON/IccMpeJson.h b/IccJSON/IccLibJSON/IccMpeJson.h new file mode 100644 index 000000000..7518f1aeb --- /dev/null +++ b/IccJSON/IccLibJSON/IccMpeJson.h @@ -0,0 +1,396 @@ +/** @file +File: IccMpeJson.h + +Contains: Header for implementation of CIccMpeJson class and + creation factories + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCMPEJSON_H +#define _ICCMPEJSON_H + +#include "IccMpeBasic.h" +#include "IccMpeACS.h" +#include "IccMpeCalc.h" +#include "IccMpeSpectral.h" +#include +#include +#include +#include + +using IccJson = nlohmann::json; + +// --------------------------------------------------------------------------- +// Base extension interface +// --------------------------------------------------------------------------- +class CIccMpeJson : public IIccExtensionMpe +{ +public: + virtual ~CIccMpeJson() {} + + virtual bool ToJson(IccJson &j) = 0; + virtual bool ParseJson(const IccJson &j, std::string &parseStr) = 0; + + virtual const char *GetExtClassName() { return "CIccMpeJson"; } +}; + +// --------------------------------------------------------------------------- +// Unknown element +// --------------------------------------------------------------------------- +class CIccMpeJsonUnknown : public CIccMpeUnknown, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonUnknown() {} + virtual const char *GetClassName() const { return "CIccMpeJsonUnknown"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Segmented curve helper (no MPE base, used by CIccMpeJsonCurveSet) +// --------------------------------------------------------------------------- +class CIccSegmentedCurveJson : public CIccSegmentedCurve +{ +public: + CIccSegmentedCurveJson() : CIccSegmentedCurve() {} + CIccSegmentedCurveJson(const CIccSegmentedCurve &parent) : CIccSegmentedCurve(parent) {} + CIccSegmentedCurveJson(const CIccSegmentedCurve *parent) : CIccSegmentedCurve(*parent) {} + + bool ToJson(IccJson &j); + bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Standard MPE elements +// --------------------------------------------------------------------------- +class CIccMpeJsonCurveSet : public CIccMpeCurveSet, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonCurveSet() {} + virtual const char *GetClassName() const { return "CIccMpeJsonCurveSet"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonTintArray : public CIccMpeTintArray, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonTintArray() {} + virtual const char *GetClassName() const { return "CIccMpeJsonTintArray"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Tone-map element +// --------------------------------------------------------------------------- +class CIccJsonToneMapFunc : public CIccToneMapFunc +{ +public: + virtual ~CIccJsonToneMapFunc() {} + virtual CIccToneMapFunc *NewCopy() const; + virtual const char *GetClassName() const { return "CIccJsonToneMapFunc"; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonToneMap : public CIccMpeToneMap, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonToneMap() {} + virtual const char *GetClassName() const { return "CIccMpeJsonToneMap"; } + virtual CIccToneMapFunc *NewToneMapFunc() { return new CIccJsonToneMapFunc(); } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonMatrix : public CIccMpeMatrix, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonMatrix() {} + virtual const char *GetClassName() const { return "CIccMpeJsonMatrix"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonCLUT : public CIccMpeCLUT, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonCLUT() {} + virtual const char *GetClassName() const { return "CIccMpeJsonCLUT"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonExtCLUT : public CIccMpeExtCLUT, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonExtCLUT() {} + virtual const char *GetClassName() const { return "CIccMpeJsonExtCLUT"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonBAcs : public CIccMpeBAcs, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonBAcs() {} + virtual const char *GetClassName() const { return "CIccMpeJsonBAcs"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonEAcs : public CIccMpeEAcs, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonEAcs() {} + virtual const char *GetClassName() const { return "CIccMpeJsonEAcs"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonJabToXYZ : public CIccMpeJabToXYZ, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonJabToXYZ() {} + virtual const char *GetClassName() const { return "CIccMpeJsonJabToXYZ"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonXYZToJab : public CIccMpeXYZToJab, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonXYZToJab() {} + virtual const char *GetClassName() const { return "CIccMpeJsonXYZToJab"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Calculator element – helper types (parallel to IccMpeXml.h) +// --------------------------------------------------------------------------- +class CIccMpePtr +{ +public: + CIccMpePtr(CIccMultiProcessElement *pMpe = NULL, int nIndex = -1) + : m_ptr(pMpe), m_nIndex(nIndex) {} + CIccMultiProcessElement *m_ptr; + int m_nIndex; +}; + +typedef std::map MpePtrMap; +typedef std::list MpePtrList; + +class CIccTempVar +{ +public: + CIccTempVar(std::string name = "", int pos = -1, icUInt16Number size = 1) + : m_name(name), m_pos(pos), m_size(size) {} + std::string m_name; + int m_pos; + icUInt16Number m_size; +}; + +typedef std::map TempVarMap; +typedef std::list TempVarList; +typedef std::pair IndexSizePair; +typedef std::map ChanVarMap; + +class CIccTempDeclVar +{ +public: + CIccTempDeclVar(std::string name = "", int pos = -1, icUInt16Number size = 1) + : m_name(name), m_pos(pos), m_size(size) {} + std::string m_name; + int m_pos; + icUInt16Number m_size; + TempVarList m_members; +}; + +typedef std::map TempDeclVarMap; +typedef std::map MacroMap; + +class CIccMpeJsonCalculator : public CIccMpeCalculator, public CIccMpeJson +{ +public: + CIccMpeJsonCalculator() : CIccMpeCalculator() { m_sImport = "*"; } + virtual ~CIccMpeJsonCalculator() { clean(); } + + virtual const char *GetClassName() const { return "CIccMpeJsonCalculator"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); + + bool ParseImport(const IccJson &j, std::string importPath, std::string &parseStr); + +protected: + void clean(); + static bool validNameChar(char c, bool bFirst); + static bool validName(const char *szName); + bool ValidMacroCalls(const char *szMacroText, std::string macroStack, std::string &parseStr) const; + bool ValidateMacroCalls(std::string &parseStr) const; + bool Flatten(std::string &flatStr, std::string macroName, const char *szFunc, + std::string &parseStr, icUInt32Number nLocalsOffset = 0); + bool UpdateLocals(std::string &func, std::string szFunc, std::string &parseStr, int nLocalsOffset); + bool ParseChanMap(ChanVarMap &chanMap, const char *szNames, int nChannels); + + ChanVarMap m_inputMap; + ChanVarMap m_outputMap; + std::string m_sImport; + TempDeclVarMap m_declVarMap; + int m_nNextVar; + TempVarMap m_varMap; + MpePtrMap m_mpeMap; + int m_nNextMpe; + MpePtrList m_mpeList; + MacroMap m_macroMap; + TempDeclVarMap m_macroLocalMap; +}; + +// --------------------------------------------------------------------------- +// Spectral elements +// --------------------------------------------------------------------------- +class CIccMpeJsonEmissionMatrix : public CIccMpeEmissionMatrix, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonEmissionMatrix() {} + virtual const char *GetClassName() const { return "CIccMpeJsonEmissionMatrix"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonInvEmissionMatrix : public CIccMpeInvEmissionMatrix, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonInvEmissionMatrix() {} + virtual const char *GetClassName() const { return "CIccMpeJsonInvEmissionMatrix"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonEmissionCLUT : public CIccMpeEmissionCLUT, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonEmissionCLUT() {} + virtual const char *GetClassName() const { return "CIccMpeJsonEmissionCLUT"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonReflectanceCLUT : public CIccMpeReflectanceCLUT, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonReflectanceCLUT() {} + virtual const char *GetClassName() const { return "CIccMpeJsonReflectanceCLUT"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonEmissionObserver : public CIccMpeEmissionObserver, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonEmissionObserver() {} + virtual const char *GetClassName() const { return "CIccMpeJsonEmissionObserver"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccMpeJsonReflectanceObserver : public CIccMpeReflectanceObserver, public CIccMpeJson +{ +public: + virtual ~CIccMpeJsonReflectanceObserver() {} + virtual const char *GetClassName() const { return "CIccMpeJsonReflectanceObserver"; } + virtual IIccExtensionMpe *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +#endif // _ICCMPEJSON_H diff --git a/IccJSON/IccLibJSON/IccMpeJsonFactory.cpp b/IccJSON/IccLibJSON/IccMpeJsonFactory.cpp new file mode 100644 index 000000000..317fd6ec3 --- /dev/null +++ b/IccJSON/IccLibJSON/IccMpeJsonFactory.cpp @@ -0,0 +1,102 @@ +/** @file + File: IccMpeJsonFactory.cpp + + Contains: Implementation of CIccMpeJsonFactory – + creates JSON-aware multi-process element objects. + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccMpeJson.h" +#include "IccMpeJsonFactory.h" +#include "IccUtil.h" +#include "IccProfile.h" + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +CIccMultiProcessElement* CIccMpeJsonFactory::CreateElement(icElemTypeSignature elemTypeSig) +{ + switch (elemTypeSig) { + case icSigMatrixElemType: return new CIccMpeJsonMatrix(); + case icSigCurveSetElemType: return new CIccMpeJsonCurveSet(); + case icSigCLutElemType: return new CIccMpeJsonCLUT(); + case icSigExtCLutElemType: return new CIccMpeJsonExtCLUT(); + case icSigCalculatorElemType: return new CIccMpeJsonCalculator(); + case icSigTintArrayElemType: return new CIccMpeJsonTintArray(); + case icSigToneMapElemType: return new CIccMpeJsonToneMap(); + case icSigXYZToJabElemType: return new CIccMpeJsonXYZToJab(); + case icSigJabToXYZElemType: return new CIccMpeJsonJabToXYZ(); + case icSigEmissionMatrixElemType: return new CIccMpeJsonEmissionMatrix(); + case icSigInvEmissionMatrixElemType: return new CIccMpeJsonInvEmissionMatrix(); + case icSigEmissionCLUTElemType: return new CIccMpeJsonEmissionCLUT(); + case icSigReflectanceCLUTElemType: return new CIccMpeJsonReflectanceCLUT(); + case icSigEmissionObserverElemType: return new CIccMpeJsonEmissionObserver(); + case icSigReflectanceObserverElemType: return new CIccMpeJsonReflectanceObserver(); + case icSigBAcsElemType: return new CIccMpeJsonBAcs(); + case icSigEAcsElemType: return new CIccMpeJsonEAcs(); + default: return new CIccMpeJsonUnknown(); + } +} + +bool CIccMpeJsonFactory::GetElementSigName(std::string & /*elemName*/, + icElemTypeSignature /*elemTypeSig*/) +{ + return false; +} + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif diff --git a/IccJSON/IccLibJSON/IccMpeJsonFactory.h b/IccJSON/IccLibJSON/IccMpeJsonFactory.h new file mode 100644 index 000000000..1f347ad63 --- /dev/null +++ b/IccJSON/IccLibJSON/IccMpeJsonFactory.h @@ -0,0 +1,95 @@ +/** @file + File: IccMpeJsonFactory.h + + Contains: Header for implementation of CIccMpeJsonFactory class – + an extension factory providing ICC JSON format capabilities + for multi-process elements. + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCMPEJSONFACTORY_H +#define _ICCMPEJSONFACTORY_H + +#include "IccMpeFactory.h" +#include "IccDefs.h" +#include +#include + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +/** + *********************************************************************** + * Class: CIccMpeJsonFactory + * + * Purpose: + * Creates JSON-aware CIccMultiProcessElement objects for all ICC-specified + * element types. Push an instance onto CIccMpeCreator to enable JSON + * serialization of MPE tags. + *********************************************************************** + */ +class CIccMpeJsonFactory : public IIccMpeFactory +{ +public: + virtual CIccMultiProcessElement* CreateElement(icElemTypeSignature elementSig); + virtual bool GetElementSigName(std::string &elemName, icElemTypeSignature elemTypeSig); +}; + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif + +#endif // _ICCMPEJSONFACTORY_H diff --git a/IccJSON/IccLibJSON/IccProfileJson.cpp b/IccJSON/IccLibJSON/IccProfileJson.cpp new file mode 100644 index 000000000..259afc863 --- /dev/null +++ b/IccJSON/IccLibJSON/IccProfileJson.cpp @@ -0,0 +1,533 @@ +/** @file + File: IccProfileJson.cpp + + Contains: Implementation of ICC Profile JSON format conversions + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccProfileJson.h" +#include "IccTagJson.h" +#include "IccUtilJson.h" +#include "IccUtil.h" +#include "IccTagFactory.h" +#include +#include +#include +#include +#include +#include + +typedef std::map IccOffsetTagSigMap; + +// --------------------------------------------------------------------------- +// JSON-specific header flags / device attributes helpers +// --------------------------------------------------------------------------- + +static IccJson icJsonGetHeaderFlags(icUInt32Number flags) +{ + IccJson j; + j["EmbeddedInFile"] = (bool)(flags & icEmbeddedProfileTrue); + j["UseWithEmbeddedDataOnly"] = (bool)(flags & icUseWithEmbeddedDataOnly); + if (flags & icExtendedRangePCS) j["ExtendedRangePCS"] = true; + if (flags & icMCSNeedsSubsetTrue) j["MCSNeedsSubset"] = true; + icUInt32Number other = flags & ~(icEmbeddedProfileTrue | icUseWithEmbeddedDataOnly | + icExtendedRangePCS | icMCSNeedsSubsetTrue); + if (other) { + char buf[16]; snprintf(buf, sizeof(buf), "%08x", other); + j["VendorFlags"] = buf; + } + return j; +} + +static icUInt32Number icJsonParseHeaderFlags(const IccJson &j) +{ + icUInt32Number flags = 0; + bool b = false; + jGetValue(j, "EmbeddedInFile", b); if (b) flags |= icEmbeddedProfileTrue; + b = false; jGetValue(j, "UseWithEmbeddedDataOnly", b); if (b) flags |= icUseWithEmbeddedDataOnly; + b = false; jGetValue(j, "ExtendedRangePCS", b); if (b) flags |= icExtendedRangePCS; + b = false; jGetValue(j, "MCSNeedsSubset", b); if (b) flags |= icMCSNeedsSubsetTrue; + std::string vendor; + jGetString(j, "VendorFlags", vendor); + if (!vendor.empty()) { + unsigned v = 0; + sscanf(vendor.c_str(), "%x", &v); + flags |= v; + } + return flags; +} + +// icJsonGetDeviceAttr and icJsonParseDeviceAttr are now in IccUtilJson + +// --------------------------------------------------------------------------- +// Serialization: profile → JSON object +// --------------------------------------------------------------------------- + +bool CIccProfileJson::ToJson(IccJson &root) +{ + CIccInfo info; + const size_t bufSize = 256; + char buf[bufSize]; + + // Header + IccJson header; + header["PreferredCMMType"] = m_Header.cmmId ? icGetSigStr(buf, bufSize, m_Header.cmmId) : ""; + header["ProfileVersion"] = info.GetVersionName(m_Header.version); + + if (m_Header.version & 0x0000ffff) + header["ProfileSubClassVersion"] = info.GetSubClassVersionName(m_Header.version); + + header["ProfileDeviceClass"] = m_Header.deviceClass ? icGetSigStr(buf, bufSize, m_Header.deviceClass) : ""; + + if (m_Header.deviceSubClass) + header["ProfileDeviceSubClass"] = icGetSigStr(buf, bufSize, m_Header.deviceSubClass); + + header["DataColourSpace"] = m_Header.colorSpace ? icGetSigStr(buf, bufSize, m_Header.colorSpace) : ""; + header["PCS"] = m_Header.pcs ? icGetSigStr(buf, bufSize, m_Header.pcs) : ""; + + // Creation date/time as ISO-8601 + char dt[64]; + snprintf(dt, sizeof(dt), "%d-%02d-%02dT%02d:%02d:%02d", + m_Header.date.year, m_Header.date.month, m_Header.date.day, + m_Header.date.hours, m_Header.date.minutes, m_Header.date.seconds); + header["CreationDateTime"] = dt; + + header["ProfileFileSignature"] = icGetSigStr(buf, bufSize, m_Header.magic); + header["PrimaryPlatform"] = m_Header.platform ? icGetSigStr(buf, bufSize, m_Header.platform) : ""; + header["CMMFlags"] = icJsonGetHeaderFlags(m_Header.flags); + header["DeviceManufacturer"] = m_Header.manufacturer ? icGetSigStr(buf, bufSize, m_Header.manufacturer) : ""; + header["DeviceModel"] = m_Header.model ? icGetSigStr(buf, bufSize, m_Header.model) : ""; + header["DeviceAttributes"] = icJsonGetDeviceAttr(m_Header.attributes); + header["RenderingIntent"] = info.GetRenderingIntentName((icRenderingIntent)m_Header.renderingIntent, m_Header.version >= icVersionNumberV5); + header["PCSIlluminant"] = IccJson::array({ + icFtoD(m_Header.illuminant.X), icFtoD(m_Header.illuminant.Y), icFtoD(m_Header.illuminant.Z) + }); + header["ProfileCreator"] = m_Header.creator ? icGetSigStr(buf, bufSize, m_Header.creator) : ""; + + // Profile ID (16-byte MD5) as hex + bool nonzero = false; + for (int i = 0; i < 16; i++) if (m_Header.profileID.ID8[i]) { nonzero = true; break; } + if (nonzero) + header["ProfileID"] = icJsonDumpHexData(m_Header.profileID.ID8, 16); + + if (m_Header.spectralPCS) + header["SpectralPCS"] = icGetColorSigStr(buf, bufSize, m_Header.spectralPCS); + + if (m_Header.spectralRange.start || m_Header.spectralRange.end || m_Header.spectralRange.steps) { + IccJson sr; + sr["start"] = (double)icF16toF(m_Header.spectralRange.start); + sr["end"] = (double)icF16toF(m_Header.spectralRange.end); + sr["steps"] = (int)m_Header.spectralRange.steps; + header["SpectralRange"] = sr; + } + if (m_Header.biSpectralRange.start || m_Header.biSpectralRange.end || m_Header.biSpectralRange.steps) { + IccJson bsr; + bsr["start"] = (double)icF16toF(m_Header.biSpectralRange.start); + bsr["end"] = (double)icF16toF(m_Header.biSpectralRange.end); + bsr["steps"] = (int)m_Header.biSpectralRange.steps; + header["BiSpectralRange"] = bsr; + } + if (m_Header.mcs) + header["MCS"] = icGetColorSigStr(buf, bufSize, m_Header.mcs); + + root["Header"] = header; + + // Tags — stored as a JSON array to preserve tag order. + // Each element is a single-member object: { "": { ... } } + // Known tags use their ICC name (e.g. "redMatrixColumnTag"). + // Private/unknown tags use "PrivateTag_N" with a "sig" member for the raw 4-char signature. + // Tag data is in "data": { "type": "", ...fields... }. + // Shared tags carry "SameAs": "" instead of a "data" entry. + IccJson tags = IccJson::array(); + std::set sigSet; + std::map ptrToFirstKey; + int privateTagCount = 0; + + for (auto i = m_Tags.begin(); i != m_Tags.end(); ++i) { + if (sigSet.count(i->TagInfo.sig)) + continue; + sigSet.insert(i->TagInfo.sig); + + CIccTag *pTag = FindTag(i->TagInfo.sig); + if (!pTag) + continue; + + // Determine the key name for this tag + const icChar *tagSigName = CIccTagCreator::GetTagSigName(i->TagInfo.sig); + bool isPrivateTag = !tagSigName; + std::string key = isPrivateTag + ? "PrivateTag_" + std::to_string(++privateTagCount) + : std::string(tagSigName); + + IccJson tagObj; + if (isPrivateTag) + tagObj["sig"] = icGetSigStr(buf, bufSize, i->TagInfo.sig); + + // SameAs: tag object already serialized under another key + auto prevIt = ptrToFirstKey.find(pTag); + if (prevIt != ptrToFirstKey.end()) { + tagObj["sameAs"] = prevIt->second; + IccJson entry; + entry[key] = tagObj; + tags.push_back(entry); + continue; + } + + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) + continue; + + CIccTagJson *pJsonTag = static_cast(pExt); + + IccJson tagData; + if (!pJsonTag->ToJson(tagData)) + continue; + + // Place the type entry in "data": { "type": "", ...fields... } + const icChar *typeName = CIccTagCreator::GetTagTypeSigName(pTag->GetType()); + if (!typeName) typeName = "PrivateType"; + tagData["type"] = typeName; + if (!strcmp(typeName, "PrivateType")) + tagData["sig"] = icGetSigStr(buf, bufSize, pTag->GetType()); + + if (pTag->m_nReserved) + tagObj["Reserved"] = (unsigned int)pTag->m_nReserved; + tagObj["data"] = tagData; + + IccJson entry; + entry[key] = tagObj; + tags.push_back(entry); + ptrToFirstKey[pTag] = key; + } + root["Tags"] = tags; + + return true; +} + +bool CIccProfileJson::ToJson(std::string &jsonString, int indent) +{ + IccJson root; + IccJson profile; + if (!ToJson(profile)) + return false; + root["IccProfile"] = profile; + jsonString = root.dump(indent); + return true; +} + +// --------------------------------------------------------------------------- +// Parsing: JSON object → profile +// --------------------------------------------------------------------------- + +static icUInt32Number icJsonParseBCDByte(const char *s) +{ + int v = atoi(s); + return (icUInt32Number)(((v / 10) % 10) * 16 + (v % 10)); +} + +static icUInt32Number icJsonParseBCDVersionStr(const char *szVer) +{ + std::string part; + for (; *szVer && *szVer != '.'; szVer++) part += *szVer; + icUInt32Number hi = icJsonParseBCDByte(part.c_str()); part.clear(); + if (*szVer) szVer++; + for (; *szVer; szVer++) part += *szVer; + icUInt32Number lo = part.empty() ? 0 : icJsonParseBCDByte(part.c_str()); + return (hi << 8) | lo; +} + +bool CIccProfileJson::ParseBasic(const IccJson &header, std::string &parseStr) +{ + CIccInfo info; + + memset(&m_Header, 0, sizeof(m_Header)); + m_Header.magic = icMagicNumber; + + std::string str; + if (jGetString(header, "PreferredCMMType", str)) + m_Header.cmmId = (icCmmSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "ProfileVersion", str)) + m_Header.version = (m_Header.version & 0x0000ffff) | (icJsonParseBCDVersionStr(str.c_str()) << 16); + + if (jGetString(header, "ProfileSubClassVersion", str)) + m_Header.version = (m_Header.version & 0xffff0000) | icJsonParseBCDVersionStr(str.c_str()); + + if (jGetString(header, "ProfileDeviceClass", str)) + m_Header.deviceClass = (icProfileClassSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "ProfileDeviceSubClass", str)) + m_Header.deviceSubClass = (icSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "DataColourSpace", str)) + m_Header.colorSpace = (icColorSpaceSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "PCS", str)) + m_Header.pcs = (icColorSpaceSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "CreationDateTime", str)) + m_Header.date = icGetDateTimeValue(str.c_str()); + + if (jGetString(header, "PrimaryPlatform", str)) + m_Header.platform = (icPlatformSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "DeviceManufacturer", str)) + m_Header.manufacturer = (icSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "DeviceModel", str)) + m_Header.model = (icUInt32Number)icGetSigVal(str.c_str()); + + if (jGetString(header, "ProfileCreator", str)) + m_Header.creator = (icSignature)icGetSigVal(str.c_str()); + + if (header.contains("CMMFlags") && header["CMMFlags"].is_object()) + m_Header.flags = icJsonParseHeaderFlags(header["CMMFlags"]); + + if (header.contains("DeviceAttributes") && header["DeviceAttributes"].is_object()) + m_Header.attributes = icJsonParseDeviceAttr(header["DeviceAttributes"]); + + if (jGetString(header, "RenderingIntent", str)) + m_Header.renderingIntent = icGetRenderingIntentValue(str.c_str()); + + double ill[3]; + if (jGetArray(header, "PCSIlluminant", ill, 3)) { + m_Header.illuminant.X = icDtoF(ill[0]); + m_Header.illuminant.Y = icDtoF(ill[1]); + m_Header.illuminant.Z = icDtoF(ill[2]); + } + + if (jGetString(header, "ProfileID", str)) + icJsonGetHexData(m_Header.profileID.ID8, str.c_str(), 16); + + if (jGetString(header, "SpectralPCS", str)) + m_Header.spectralPCS = (icColorSpaceSignature)icGetSigVal(str.c_str()); + + auto parseSpectralRange = [&](const char *field, icSpectralRange &range) { + if (header.contains(field) && header[field].is_object()) { + const IccJson &sr = header[field]; + double start = 0, end = 0; int steps = 0; + jGetValue(sr, "start", start); + jGetValue(sr, "end", end); + jGetValue(sr, "steps", steps); + range.start = icFtoF16((icFloat32Number)start); + range.end = icFtoF16((icFloat32Number)end); + range.steps = (icUInt16Number)steps; + } + }; + parseSpectralRange("SpectralRange", m_Header.spectralRange); + parseSpectralRange("BiSpectralRange", m_Header.biSpectralRange); + + if (jGetString(header, "MCS", str)) + m_Header.mcs = (icMultiplexColorSignature)icGetSigVal(str.c_str()); + + if (jGetString(header, "ProfileSubClass", str)) + m_Header.deviceSubClass = (icSignature)icGetSigVal(str.c_str()); + + return true; +} + +bool CIccProfileJson::ParseTag(const std::string &key, const IccJson &tagValue, + std::map &keyToSig, + std::string &parseStr) +{ + // Determine tag signature from the key name + icTagSignature sig = CIccTagCreator::GetTagNameSig(key.c_str()); + if (sig == icSigUnknownTag) { + // Private tag — raw sig stored in "sig" member + if (!tagValue.contains("sig")) { + parseStr += "Private tag '" + key + "' missing 'sig'\n"; + return false; + } + sig = (icTagSignature)icGetSigVal(tagValue["sig"].get().c_str()); + } + + // sameAs: re-attach an already-parsed tag under this signature + if (tagValue.contains("sameAs")) { + std::string refKey = tagValue["sameAs"].get(); + auto it = keyToSig.find(refKey); + if (it == keyToSig.end()) { + parseStr += "sameAs references unknown tag '" + refKey + "' for '" + key + "'\n"; + return false; + } + CIccTag *pRefTag = FindTag(it->second); + if (!pRefTag) { + parseStr += "sameAs target not found: '" + refKey + "'\n"; + return false; + } + if (!AttachTag(sig, pRefTag)) { + parseStr += "Unable to attach sameAs tag '" + key + "'\n"; + return false; + } + keyToSig[key] = sig; + return true; + } + + // "data" member of the tag holds a flat object: { "type": "", ...fields... } + if (!tagValue.contains("data") || !tagValue["data"].is_object()) { + parseStr += "Tag '" + key + "' missing 'data' object\n"; + return false; + } + const IccJson &tagData = tagValue["data"]; + + // Read the "type" discriminator field + std::string typeName; + jGetString(tagData, "type", typeName); + if (typeName.empty()) { + parseStr += "Tag '" + key + "' has no 'type' field in 'data'\n"; + return false; + } + + // Resolve type sig from name; for "PrivateType" use the "sig" field + icTagTypeSignature typeSig = CIccTagCreator::GetTagTypeNameSig(typeName.c_str()); + if (typeSig == icSigUnknownType) { + if (typeName == "PrivateType" && tagData.contains("sig")) + typeSig = (icTagTypeSignature)icGetSigVal(tagData["sig"].get().c_str()); + else + typeSig = (icTagTypeSignature)icGetSigVal(typeName.c_str()); + } + + CIccTag *pTag = CIccTagCreator::CreateTag(typeSig); + if (!pTag) { + parseStr += "Unable to create type '" + typeName + "' for tag '" + key + "'\n"; + return false; + } + + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) { + delete pTag; + parseStr += "Type '" + typeName + "' does not support JSON\n"; + return false; + } + + CIccTagJson *pJsonTag = static_cast(pExt); + if (!pJsonTag->ParseJson(tagData, parseStr)) { + delete pTag; + return false; + } + + if (tagValue.contains("Reserved")) + pTag->m_nReserved = (icUInt32Number)tagValue["Reserved"].get(); + + if (!AttachTag(sig, pTag)) { + delete pTag; + parseStr += "Unable to attach tag '" + key + "'\n"; + return false; + } + + keyToSig[key] = sig; + return true; +} + +bool CIccProfileJson::ParseJson(const IccJson &root, std::string &parseStr) +{ + // The root may be the IccProfile object directly or wrapped in {"IccProfile": ...} + const IccJson *pProfile = &root; + IccJson unwrapped; + if (root.contains("IccProfile")) { + unwrapped = root["IccProfile"]; + pProfile = &unwrapped; + } + + if (!pProfile->contains("Header") || !pProfile->contains("Tags")) { + parseStr += "Missing Header or Tags in JSON profile\n"; + return false; + } + + if (!ParseBasic((*pProfile)["Header"], parseStr)) + return false; + + const IccJson &tags = (*pProfile)["Tags"]; + if (!tags.is_array()) { + parseStr += "Tags must be a JSON array\n"; + return false; + } + + std::map keyToSig; + for (const auto &entry : tags) { + if (!entry.is_object() || entry.size() != 1) { + parseStr += "Warning: tag entry must be a single-member object, skipping\n"; + continue; + } + auto it = entry.begin(); + const std::string &key = it.key(); + const IccJson &value = it.value(); + if (!ParseTag(key, value, keyToSig, parseStr)) + parseStr += "Warning: skipped tag '" + key + "'\n"; + } + return true; +} + +bool CIccProfileJson::LoadJson(const char *szFilename, std::string *parseStr) +{ + std::ifstream f(szFilename); + if (!f.is_open()) { + if (parseStr) *parseStr += std::string("Unable to open file: ") + szFilename + "\n"; + return false; + } + + IccJson root; + try { + f >> root; + } + catch (const std::exception &e) { + if (parseStr) *parseStr += std::string("JSON parse error: ") + e.what() + "\n"; + return false; + } + + std::string reason; + bool ok = ParseJson(root, reason); + if (parseStr) *parseStr += reason; + return ok; +} diff --git a/IccJSON/IccLibJSON/IccProfileJson.h b/IccJSON/IccLibJSON/IccProfileJson.h new file mode 100644 index 000000000..5c5c083bc --- /dev/null +++ b/IccJSON/IccLibJSON/IccProfileJson.h @@ -0,0 +1,94 @@ +/** @file +File: IccProfileJson.h + +Contains: Header for implementation of the CIccProfileJson class. + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#if !defined(_ICCPROFILEJSON_H) +#define _ICCPROFILEJSON_H + +#include "IccProfile.h" +#include +#include +#include + +using IccJson = nlohmann::json; + +class CIccProfileJson : public CIccProfile +{ +public: + CIccProfileJson() : CIccProfile() {} + CIccProfileJson(const CIccProfileJson &profile) : CIccProfile(profile) {} + virtual CIccProfile* NewCopy() const { return new CIccProfileJson(*this); } + virtual CIccProfile* NewProfile() const { return new CIccProfileJson(); } + virtual ~CIccProfileJson() {} + + virtual const char *GetClassName() const { return "CIccProfileJson"; } + + bool ToJson(std::string &jsonString, int indent = 2); + bool ToJson(IccJson &j); + + bool ParseJson(const IccJson &j, std::string &parseStr); + bool LoadJson(const char *szFilename, std::string *parseStr = NULL); + +protected: + bool ParseBasic(const IccJson &j, std::string &parseStr); + bool ParseTag(const std::string &key, const IccJson &tagValue, + std::map &keyToSig, + std::string &parseStr); +}; + +#endif /* _ICCPROFILEJSON_H */ diff --git a/IccJSON/IccLibJSON/IccTagJson.cpp b/IccJSON/IccLibJSON/IccTagJson.cpp new file mode 100644 index 000000000..929ae2f44 --- /dev/null +++ b/IccJSON/IccLibJSON/IccTagJson.cpp @@ -0,0 +1,2757 @@ +/** @file + File: IccTagJson.cpp + + Contains: Implementation of ICC tag JSON format conversions + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccTagJson.h" +#include "IccMpeJson.h" +#include "IccIoJson.h" +#include "IccUtil.h" +#include "IccUtilJson.h" +#include "IccSparseMatrix.h" +#include "IccProfileJson.h" +#include "IccStructFactory.h" +#include "IccArrayFactory.h" +#include "IccMpeFactory.h" +#include +#include +#include +#include +#include +#include + +typedef std::map IccOffsetTagSigMap; + +// Forward declarations for helpers defined later in this file +static IccJson dictLocalizedToJson(const CIccTagMultiLocalizedUnicode *pTag); +static CIccTagMultiLocalizedUnicode *dictLocalizedFromJson(const IccJson &arr); + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +// --------------------------------------------------------------------------- +// Helper: 4-char signature to string +// --------------------------------------------------------------------------- +static std::string sigToStr(icUInt32Number sig) +{ + char buf[32]; + icGetSigStr(buf, sizeof(buf), sig); + return std::string(buf); +} + +// =========================================================================== +// CIccTagJsonUnknown +// =========================================================================== + +bool CIccTagJsonUnknown::ToJson(IccJson &j) +{ + j["unknownData"] = icJsonDumpHexData(m_pData, m_nSize); + return true; +} + +bool CIccTagJsonUnknown::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string hex; + if (jGetString(j, "unknownData", hex)) { + m_nSize = icJsonGetHexDataSize(hex.c_str()); + if (m_pData) { delete[] m_pData; m_pData = NULL; } + if (m_nSize) { + m_pData = new icUInt8Number[m_nSize]; + icJsonGetHexData(m_pData, hex.c_str(), m_nSize); + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonText +// =========================================================================== + +bool CIccTagJsonText::ToJson(IccJson &j) +{ + j["text"] = m_szText ? m_szText : ""; + return true; +} + +bool CIccTagJsonText::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string text; + if (jGetString(j, "text", text)) + SetText(text.c_str()); + return true; +} + +// =========================================================================== +// CIccTagJsonUtf8Text +// =========================================================================== + +bool CIccTagJsonUtf8Text::ToJson(IccJson &j) +{ + if (m_szText) + j["text"] = std::string((const char*)m_szText); + else + j["text"] = ""; + return true; +} + +bool CIccTagJsonUtf8Text::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string s; + if (jGetString(j, "text", s)) + SetText(s.c_str()); + return true; +} + +// =========================================================================== +// CIccTagJsonZipUtf8Text +// =========================================================================== + +bool CIccTagJsonZipUtf8Text::ToJson(IccJson &j) +{ + j["compressedData"] = icJsonDumpHexData(m_pZipBuf, m_nBufSize); + return true; +} + +bool CIccTagJsonZipUtf8Text::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string hex; + if (jGetString(j, "compressedData", hex)) { + icUInt32Number sz = icJsonGetHexDataSize(hex.c_str()); + icUChar *pBuf = AllocBuffer(sz); + if (!pBuf) return false; + icJsonGetHexData(pBuf, hex.c_str(), sz); + } + return true; +} + +// =========================================================================== +// CIccTagJsonZipXml +// =========================================================================== + +bool CIccTagJsonZipXml::ToJson(IccJson &j) +{ + j["compressedData"] = icJsonDumpHexData(m_pZipBuf, m_nBufSize); + return true; +} + +bool CIccTagJsonZipXml::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string hex; + if (jGetString(j, "compressedData", hex)) { + icUInt32Number sz = icJsonGetHexDataSize(hex.c_str()); + icUChar *pBuf = AllocBuffer(sz); + if (!pBuf) return false; + icJsonGetHexData(pBuf, hex.c_str(), sz); + } + return true; +} + +// =========================================================================== +// CIccTagJsonUtf16Text +// =========================================================================== + +bool CIccTagJsonUtf16Text::ToJson(IccJson &j) +{ + // Convert UTF-16 to UTF-8 for JSON + std::string utf8; + if (m_szText) { + const icUInt16Number *p = m_szText; + // Simple 2-byte to string conversion (ASCII range only for skeleton) + while (*p) { + if (*p < 128) utf8 += (char)(*p); + else utf8 += '?'; + p++; + } + } + j["text"] = utf8; + return true; +} + +bool CIccTagJsonUtf16Text::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string s; + if (jGetString(j, "text", s)) + SetText(s.c_str()); + return true; +} + +// =========================================================================== +// CIccTagJsonTextDescription +// =========================================================================== + +bool CIccTagJsonTextDescription::ToJson(IccJson &j) +{ + j["description"] = m_szText ? m_szText : ""; + return true; +} + +bool CIccTagJsonTextDescription::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string desc; + if (jGetString(j, "description", desc)) + SetText(desc.c_str()); + return true; +} + +// =========================================================================== +// CIccTagJsonSignature +// =========================================================================== + +bool CIccTagJsonSignature::ToJson(IccJson &j) +{ + j["signature"] = sigToStr(m_nSig); + return true; +} + +bool CIccTagJsonSignature::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string sig; + jGetString(j, "signature", sig); + if (!sig.empty()) + m_nSig = (icSignature)icGetSigVal(sig.c_str()); + return true; +} + +// =========================================================================== +// CIccTagJsonDateTime +// =========================================================================== + +bool CIccTagJsonDateTime::ToJson(IccJson &j) +{ + char buf[32]; + snprintf(buf, sizeof(buf), "%d-%02d-%02dT%02d:%02d:%02d", + m_DateTime.year, m_DateTime.month, m_DateTime.day, + m_DateTime.hours, m_DateTime.minutes, m_DateTime.seconds); + j["dateTime"] = buf; + return true; +} + +bool CIccTagJsonDateTime::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string dt; + jGetString(j, "dateTime", dt); + m_DateTime = icGetDateTimeValue(dt.c_str()); + return true; +} + +// =========================================================================== +// CIccTagJsonXYZ +// =========================================================================== + +bool CIccTagJsonXYZ::ToJson(IccJson &j) +{ + IccJson arr = IccJson::array(); + if (m_XYZ) { + for (icUInt32Number i = 0; i < m_nSize; i++) { + arr.push_back(IccJson::array({ + (double)icFtoD(m_XYZ[i].X), + (double)icFtoD(m_XYZ[i].Y), + (double)icFtoD(m_XYZ[i].Z) + })); + } + } + j["XYZ"] = arr; + return true; +} + +bool CIccTagJsonXYZ::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (!jsonExistsField(j, "XYZ") || !j["XYZ"].is_array()) return false; + const IccJson &arr = j["XYZ"]; + if (!SetSize((icUInt32Number)arr.size())) return false; + for (size_t i = 0; i < arr.size(); i++) { + if (arr[i].is_array() && arr[i].size() >= 3) { + double xyz[3] = {0, 0, 0}; + jsonToArray(arr[i], xyz, 3); + m_XYZ[i].X = icDtoF(xyz[0]); + m_XYZ[i].Y = icDtoF(xyz[1]); + m_XYZ[i].Z = icDtoF(xyz[2]); + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonChromaticity +// =========================================================================== + +bool CIccTagJsonChromaticity::ToJson(IccJson &j) +{ + j["colorantType"] = (int)m_nColorantType; + IccJson channels = IccJson::array(); + for (icUInt16Number i = 0; i < m_nChannels; i++) + channels.push_back(IccJson::array({ icUFtoD(m_xy[i].x), icUFtoD(m_xy[i].y) })); + j["channels"] = channels; + return true; +} + +bool CIccTagJsonChromaticity::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + int colorantType = 0; + jGetValue(j, "colorantType", colorantType); + m_nColorantType = (icColorantEncoding)colorantType; + if (jsonExistsField(j, "channels") && j["channels"].is_array()) { + const IccJson &ch = j["channels"]; + if (!SetSize((icUInt16Number)ch.size())) return false; + for (size_t i = 0; i < ch.size(); i++) { + if (ch[i].is_array() && ch[i].size() >= 2) { + double xy[2] = {0, 0}; + jsonToArray(ch[i], xy, 2); + m_xy[i].x = icDtoUF(xy[0]); + m_xy[i].y = icDtoUF(xy[1]); + } + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonCicp +// =========================================================================== + +bool CIccTagJsonCicp::ToJson(IccJson &j) +{ + j["colourPrimaries"] = (int)m_nColorPrimaries; + j["transferCharacteristics"] = (int)m_nTransferCharacteristics; + j["matrixCoefficients"] = (int)m_nMatrixCoefficients; + j["videoFullRangeFlag"] = (int)m_nVideoFullRangeFlag; + return true; +} + +bool CIccTagJsonCicp::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + m_nColorPrimaries = 0; jGetValue(j, "colourPrimaries", m_nColorPrimaries); + m_nTransferCharacteristics = 0; jGetValue(j, "transferCharacteristics", m_nTransferCharacteristics); + m_nMatrixCoefficients = 0; jGetValue(j, "matrixCoefficients", m_nMatrixCoefficients); + m_nVideoFullRangeFlag = 0; jGetValue(j, "videoFullRangeFlag", m_nVideoFullRangeFlag); + return true; +} + +// =========================================================================== +// CIccTagJsonMeasurement +// =========================================================================== + +bool CIccTagJsonMeasurement::ToJson(IccJson &j) +{ + CIccInfo info; + j["standardObserver"] = (int)m_Data.stdObserver; + j["backing"] = IccJson::array({ icFtoD(m_Data.backing.X), icFtoD(m_Data.backing.Y), icFtoD(m_Data.backing.Z) }); + j["geometry"] = (int)m_Data.geometry; + j["flare"] = info.GetMeasurementFlareName(m_Data.flare); + j["illuminant"] = (int)m_Data.illuminant; + return true; +} + +bool CIccTagJsonMeasurement::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + int stdObserver = 0, geometry = 0, illuminant = 0; + std::string flare; + double backing[3] = {0, 0, 0}; + jGetValue(j, "standardObserver", stdObserver); + jGetArray(j, "backing", backing, 3); + jGetValue(j, "geometry", geometry); + jGetString(j, "flare", flare); + jGetValue(j, "illuminant",illuminant); + m_Data.stdObserver = (icStandardObserver)stdObserver; + m_Data.backing.X = icDtoF(backing[0]); + m_Data.backing.Y = icDtoF(backing[1]); + m_Data.backing.Z = icDtoF(backing[2]); + m_Data.geometry = (icMeasurementGeometry)geometry; + if (flare == "Flare 100") + m_Data.flare = icFlare100; + else + m_Data.flare = icFlare0; + m_Data.illuminant = (icIlluminant)illuminant; + return true; +} + +// =========================================================================== +// CIccTagJsonViewingConditions +// =========================================================================== + +bool CIccTagJsonViewingConditions::ToJson(IccJson &j) +{ + j["illuminant"] = IccJson::array({ icFtoD(m_XYZIllum.X), icFtoD(m_XYZIllum.Y), icFtoD(m_XYZIllum.Z) }); + j["surround"] = IccJson::array({ icFtoD(m_XYZSurround.X), icFtoD(m_XYZSurround.Y), icFtoD(m_XYZSurround.Z) }); + j["illuminantType"] = (int)m_illumType; + return true; +} + +bool CIccTagJsonViewingConditions::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + double illum[3] = {0, 0, 0}; + double surround[3] = {0, 0, 0}; + int illuminantType = 0; + jGetArray(j, "illuminant", illum, 3); + jGetArray(j, "surround", surround, 3); + jGetValue(j, "illuminantType", illuminantType); + m_XYZIllum.X = icDtoF(illum[0]); + m_XYZIllum.Y = icDtoF(illum[1]); + m_XYZIllum.Z = icDtoF(illum[2]); + m_XYZSurround.X = icDtoF(surround[0]); + m_XYZSurround.Y = icDtoF(surround[1]); + m_XYZSurround.Z = icDtoF(surround[2]); + m_illumType = (icIlluminant)illuminantType; + return true; +} + +// =========================================================================== +// CIccTagJsonSpectralDataInfo +// =========================================================================== + +bool CIccTagJsonSpectralDataInfo::ToJson(IccJson &j) +{ + j["spectralColorSig"] = sigToStr(m_nSig); + j["spectralRange"] = IccJson::array({ icF16toF(m_spectralRange.start), icF16toF(m_spectralRange.end), (int)m_spectralRange.steps }); + j["biSpectralRange"] = IccJson::array({ icF16toF(m_biSpectralRange.start), icF16toF(m_biSpectralRange.end), (int)m_biSpectralRange.steps }); + return true; +} + +bool CIccTagJsonSpectralDataInfo::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + std::string sig; + jGetString(j, "spectralColorSig", sig); + m_nSig = (icSignature)icGetSigVal(sig.c_str()); + if (jsonExistsField(j, "spectralRange") && j["spectralRange"].is_array() && j["spectralRange"].size() >= 3) { + double r[3] = {0, 0, 0}; + jsonToArray(j["spectralRange"], r, 3); + m_spectralRange.start = icFtoF16((icFloat32Number)r[0]); + m_spectralRange.end = icFtoF16((icFloat32Number)r[1]); + m_spectralRange.steps = (icUInt16Number)r[2]; + } + if (jsonExistsField(j, "biSpectralRange") && j["biSpectralRange"].is_array() && j["biSpectralRange"].size() >= 3) { + double r[3] = {0, 0, 0}; + jsonToArray(j["biSpectralRange"], r, 3); + m_biSpectralRange.start = icFtoF16((icFloat32Number)r[0]); + m_biSpectralRange.end = icFtoF16((icFloat32Number)r[1]); + m_biSpectralRange.steps = (icUInt16Number)r[2]; + } + return true; +} + +// =========================================================================== +// CIccTagJsonSpectralViewingConditions +// =========================================================================== + +bool CIccTagJsonSpectralViewingConditions::ToJson(IccJson &j) +{ + CIccInfo info; + j["StdObserver"] = info.GetStandardObserverName(m_stdObserver); + j["IlluminantXYZ"] = IccJson::array({ (double)m_illuminantXYZ.X, + (double)m_illuminantXYZ.Y, + (double)m_illuminantXYZ.Z }); + + if (m_observer && m_observerRange.steps) { + IccJson obs; + obs["start"] = (double)icF16toF(m_observerRange.start); + obs["end"] = (double)icF16toF(m_observerRange.end); + obs["steps"] = (int)m_observerRange.steps; + if (m_reserved2) + obs["Reserved"] = (int)m_reserved2; + IccJson data = IccJson::array(); + icUInt32Number nTotal = (icUInt32Number)m_observerRange.steps * 3; + for (icUInt32Number i = 0; i < nTotal; i++) + data.push_back((double)m_observer[i]); + obs["data"] = data; + j["ObserverFuncs"] = obs; + } + + j["StdIlluminant"] = info.GetIlluminantName(m_stdIlluminant); + j["ColorTemperature"] = (double)m_colorTemperature; + + if (m_illuminant && m_illuminantRange.steps) { + IccJson illum; + illum["start"] = (double)icF16toF(m_illuminantRange.start); + illum["end"] = (double)icF16toF(m_illuminantRange.end); + illum["steps"] = (int)m_illuminantRange.steps; + if (m_reserved3) + illum["Reserved"] = (int)m_reserved3; + IccJson data = IccJson::array(); + for (int i = 0; i < (int)m_illuminantRange.steps; i++) + data.push_back((double)m_illuminant[i]); + illum["data"] = data; + j["IlluminantSPD"] = illum; + } + + j["SurroundXYZ"] = IccJson::array({ (double)m_surroundXYZ.X, + (double)m_surroundXYZ.Y, + (double)m_surroundXYZ.Z }); + return true; +} + +bool CIccTagJsonSpectralViewingConditions::ParseJson(const IccJson &j, std::string &parseStr) +{ + memset(&m_illuminantXYZ, 0, sizeof(m_illuminantXYZ)); + memset(&m_surroundXYZ, 0, sizeof(m_surroundXYZ)); + m_stdObserver = icStdObs1931TwoDegrees; + m_stdIlluminant = icIlluminantD50; + m_colorTemperature = 5000.0f; + m_reserved2 = 0; + m_reserved3 = 0; + + std::string obsStr; + jGetString(j, "StdObserver", obsStr); + m_stdObserver = obsStr.empty() ? icStdObs1931TwoDegrees + : icGetNamedStandardObserverValue(obsStr.c_str()); + + icFloatNumber xyz[3] = { 0.0f, 0.0f, 0.0f }; + jGetArray(j, "IlluminantXYZ", xyz, 3); + m_illuminantXYZ.X = xyz[0]; + m_illuminantXYZ.Y = xyz[1]; + m_illuminantXYZ.Z = xyz[2]; + + // Optional ObserverFuncs + if (jsonExistsField(j, "ObserverFuncs") && j["ObserverFuncs"].is_object()) { + const IccJson &obs = j["ObserverFuncs"]; + icFloat32Number start = 0.0f, end = 0.0f; + int steps = 0; + jGetValue(obs, "start", start); + jGetValue(obs, "end", end); + jGetValue(obs, "steps", steps); + unsigned int res2 = 0; + jGetValue(obs, "Reserved", res2); + m_observerRange.start = icFtoF16(start); + m_observerRange.end = icFtoF16(end); + m_observerRange.steps = (icUInt16Number)steps; + m_reserved2 = (icUInt16Number)res2; + + if (jsonExistsField(obs, "data") && obs["data"].is_array()) { + const IccJson &data = obs["data"]; + icUInt32Number nExpected = (icUInt32Number)steps * 3; + if ((icUInt32Number)data.size() != nExpected) { + parseStr += "ObserverFuncs data size mismatch\n"; + return false; + } + if (m_observer) { delete[] m_observer; } + m_observer = new icFloatNumber[nExpected]; + for (icUInt32Number i = 0; i < nExpected; i++) + m_observer[i] = (icFloatNumber)data[i].get(); + } + else { + setObserver(m_stdObserver, m_observerRange, NULL); + } + } + + std::string illumStr; + jGetString(j, "StdIlluminant", illumStr); + m_stdIlluminant = illumStr.empty() ? icIlluminantD50 + : icGetIlluminantValue(illumStr.c_str()); + + jGetValue(j, "ColorTemperature", m_colorTemperature); + + // Optional IlluminantSPD + if (jsonExistsField(j, "IlluminantSPD") && j["IlluminantSPD"].is_object()) { + const IccJson &illum = j["IlluminantSPD"]; + icFloat32Number start = 0.0f, end = 0.0f; + int steps = 0; + jGetValue(illum, "start", start); + jGetValue(illum, "end", end); + jGetValue(illum, "steps", steps); + unsigned int res3 = 0; + jGetValue(illum, "Reserved", res3); + m_illuminantRange.start = icFtoF16(start); + m_illuminantRange.end = icFtoF16(end); + m_illuminantRange.steps = (icUInt16Number)steps; + m_reserved3 = (icUInt16Number)res3; + + if (jsonExistsField(illum, "data") && illum["data"].is_array()) { + const IccJson &data = illum["data"]; + if ((int)data.size() != steps) { + parseStr += "IlluminantSPD data size mismatch\n"; + return false; + } + if (m_illuminant) { delete[] m_illuminant; } + m_illuminant = new icFloatNumber[steps]; + for (int i = 0; i < steps; i++) + m_illuminant[i] = (icFloatNumber)data[i].get(); + } + else { + setIlluminant(m_stdIlluminant, m_illuminantRange, NULL, m_colorTemperature); + } + } + + icFloatNumber sxyz[3] = { 0.0f, 0.0f, 0.0f }; + jGetArray(j, "SurroundXYZ", sxyz, 3); + m_surroundXYZ.X = sxyz[0]; + m_surroundXYZ.Y = sxyz[1]; + m_surroundXYZ.Z = sxyz[2]; + + return true; +} + +// =========================================================================== +// CIccTagJsonNamedColor2 +// =========================================================================== + +bool CIccTagJsonNamedColor2::ToJson(IccJson &j) +{ + j["vendorFlag"] = (int)m_nVendorFlags; + j["countOfDeviceCoords"] = (int)m_nDeviceCoords; + j["colorantPrefix"] = m_szPrefix ? m_szPrefix : ""; + j["colorantSuffix"] = m_szSufix ? m_szSufix : ""; + + IccJson colors = IccJson::array(); + if (m_NamedColor) { + for (icUInt32Number i = 0; i < m_nSize; i++) { + IccJson c; + c["name"] = m_NamedColor[i].rootName; + c["pcsCoords"] = IccJson::array({ m_NamedColor[i].pcsCoords[0], m_NamedColor[i].pcsCoords[1], m_NamedColor[i].pcsCoords[2] }); + IccJson dev = IccJson::array(); + for (icUInt32Number d = 0; d < m_nDeviceCoords; d++) + dev.push_back((int)(m_NamedColor[i].deviceCoords[d] * 65535.0f + 0.5f)); + c["deviceCoords"] = dev; + colors.push_back(c); + } + } + j["colors"] = colors; + return true; +} + +bool CIccTagJsonNamedColor2::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + icUInt32Number nDevCoords = 0; + jGetValue(j, "countOfDeviceCoords", nDevCoords); + icUInt32Number nColors = (jsonExistsField(j, "colors") && j["colors"].is_array()) + ? (icUInt32Number)j["colors"].size() : 0; + + if (!SetSize(nColors, nDevCoords)) return false; + + jGetValue(j, "vendorFlag", m_nVendorFlags); + std::string prefix, suffix; + if (jGetString(j, "colorantPrefix", prefix)) + strncpy(m_szPrefix, prefix.c_str(), sizeof(m_szPrefix)-1); + if (jGetString(j, "colorantSuffix", suffix)) + strncpy(m_szSufix, suffix.c_str(), sizeof(m_szSufix)-1); + + if (jsonExistsField(j, "colors") && j["colors"].is_array()) { + const IccJson &colors = j["colors"]; + for (icUInt32Number i = 0; i < nColors && i < (icUInt32Number)colors.size(); i++) { + const IccJson &c = colors[i]; + std::string name; + if (jGetString(c, "name", name)) + strncpy(m_NamedColor[i].rootName, name.c_str(), sizeof(m_NamedColor[i].rootName)-1); + if (jsonExistsField(c, "pcsCoords") && c["pcsCoords"].is_array() && c["pcsCoords"].size() >= 3) { + jGetArray(c, "pcsCoords", m_NamedColor[i].pcsCoords, 3); + } + if (jsonExistsField(c, "deviceCoords") && c["deviceCoords"].is_array()) { + for (icUInt32Number d = 0; d < nDevCoords && d < (icUInt32Number)c["deviceCoords"].size(); d++) + m_NamedColor[i].deviceCoords[d] = (icFloatNumber)c["deviceCoords"][d].get() / 65535.0f; + } + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonColorantOrder +// =========================================================================== + +bool CIccTagJsonColorantOrder::ToJson(IccJson &j) +{ + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < m_nCount; i++) + arr.push_back((int)m_pData[i]); + j["colorantOrder"] = arr; + return true; +} + +bool CIccTagJsonColorantOrder::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (jsonExistsField(j, "colorantOrder") && j["colorantOrder"].is_array()) { + const IccJson &arr = j["colorantOrder"]; + if (!SetSize((icUInt32Number)arr.size())) return false; + for (size_t i = 0; i < arr.size(); i++) + m_pData[i] = (icUInt8Number)arr[i].get(); + } + return true; +} + +// =========================================================================== +// CIccTagJsonColorantTable +// =========================================================================== + +bool CIccTagJsonColorantTable::ToJson(IccJson &j) +{ + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < m_nCount; i++) { + IccJson c; + c["name"] = m_pData[i].name; + c["pcs"] = IccJson::array({ m_pData[i].data[0], m_pData[i].data[1], m_pData[i].data[2] }); + arr.push_back(c); + } + j["colorantTable"] = arr; + return true; +} + +bool CIccTagJsonColorantTable::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (jsonExistsField(j, "colorantTable") && j["colorantTable"].is_array()) { + const IccJson &arr = j["colorantTable"]; + if (!SetSize((icUInt32Number)arr.size())) return false; + for (size_t i = 0; i < arr.size(); i++) { + const IccJson &c = arr[i]; + std::string name; + if (jGetString(c, "name", name)) + strncpy(m_pData[i].name, name.c_str(), sizeof(m_pData[i].name)-1); + if (jsonExistsField(c, "pcs") && c["pcs"].is_array() && c["pcs"].size() >= 3) + jGetArray(c, "pcs", m_pData[i].data, 3); + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonSparseMatrixArray +// =========================================================================== + +bool CIccTagJsonSparseMatrixArray::ToJson(IccJson &j) +{ + j["outputChannels"] = (int)m_nChannelsPerMatrix; + j["matrixType"] = (int)m_nMatrixType; + + CIccSparseMatrix mtx; + icUInt32Number bytesPerMatrix = GetBytesPerMatrix(); + IccJson matrices = IccJson::array(); + + for (icUInt32Number i = 0; i < m_nSize; i++) { + mtx.Reset(m_RawData + i * bytesPerMatrix, bytesPerMatrix, icSparseMatrixFloatNum, true); + + IccJson jMtx; + jMtx["rows"] = (int)mtx.Rows(); + jMtx["cols"] = (int)mtx.Cols(); + + IccJson jRows = IccJson::array(); + for (int r = 0; r < (int)mtx.Rows(); r++) { + int n = mtx.GetNumRowColumns(r); + IccJson jRow; + IccJson jIdx = IccJson::array(); + IccJson jData = IccJson::array(); + + icUInt16Number *cols = mtx.GetColumnsForRow(r); + icFloatNumber *data = (icFloatNumber*)mtx.GetData()->getPtr(mtx.GetRowOffset(r)); + for (int k = 0; k < n; k++) { + jIdx.push_back(cols[k]); + jData.push_back(data[k]); + } + jRow["colIndices"] = jIdx; + jRow["colData"] = jData; + jRows.push_back(jRow); + } + jMtx["sparseRows"] = jRows; + matrices.push_back(jMtx); + } + j["matrices"] = matrices; + return true; +} + +bool CIccTagJsonSparseMatrixArray::ParseJson(const IccJson &j, std::string &parseStr) +{ + if (!j.contains("outputChannels") || !j.contains("matrixType") || !j.contains("matrices")) { + parseStr += "Missing outputChannels, matrixType, or matrices in SparseMatrixArray\n"; + return false; + } + + icUInt16Number nChannels = (icUInt16Number)j["outputChannels"].get(); + icSparseMatrixType nMatrixType = (icSparseMatrixType)j["matrixType"].get(); + const IccJson &jMats = j["matrices"]; + + if (!jMats.is_array()) { + parseStr += "matrices must be a JSON array\n"; + return false; + } + + icUInt32Number n = (icUInt32Number)jMats.size(); + if (!Reset(n, nChannels)) { + parseStr += "Failed to reset SparseMatrixArray\n"; + return false; + } + m_nMatrixType = nMatrixType; + + icUInt32Number bytesPerMatrix = GetBytesPerMatrix(); + CIccSparseMatrix mtx; + + for (icUInt32Number i = 0; i < n; i++) { + const IccJson &jMtx = jMats[i]; + if (!jMtx.contains("rows") || !jMtx.contains("cols") || !jMtx.contains("sparseRows")) { + parseStr += "SparseMatrix missing rows, cols, or sparseRows\n"; + return false; + } + + icUInt16Number nRows = (icUInt16Number)jMtx["rows"].get(); + icUInt16Number nCols = (icUInt16Number)jMtx["cols"].get(); + + mtx.Reset(m_RawData + i * bytesPerMatrix, bytesPerMatrix, icSparseMatrixFloatNum, false); + mtx.Init(nRows, nCols, true); + + icUInt16Number *rowstart = mtx.GetRowStart(); + icUInt32Number nMaxEntries = mtx.GetMaxEntries(); + const IccJson &jRows = jMtx["sparseRows"]; + icUInt32Number pos = 0; + + for (int r = 0; r < (int)nRows && r < (int)jRows.size(); r++) { + const IccJson &jRow = jRows[r]; + rowstart[r] = (icUInt16Number)pos; + + if (!jRow.contains("colIndices") || !jRow.contains("colData")) continue; + + const IccJson &jIdx = jRow["colIndices"]; + const IccJson &jData = jRow["colData"]; + + if (!jIdx.is_array() || !jData.is_array() || jIdx.size() != jData.size()) { + parseStr += "SparseRow colIndices/colData mismatch or not arrays\n"; + return false; + } + + icUInt32Number cnt = (icUInt32Number)jIdx.size(); + if (pos + cnt > nMaxEntries) { + parseStr += "Exceeded maximum number of sparse matrix entries\n"; + return false; + } + + icUInt16Number *dstCols = mtx.GetColumnsForRow(r); + icFloatNumber *dstData = (icFloatNumber*)mtx.GetData()->getPtr(pos); + for (icUInt32Number k = 0; k < cnt; k++) { + dstCols[k] = jIdx[k].get(); + dstData[k] = jData[k].get(); + } + pos += cnt; + } + // fill remaining row-start sentinels (including end sentinel at nRows) + for (int r = (int)jRows.size(); r <= (int)nRows; r++) + rowstart[r] = (icUInt16Number)pos; + } + return true; +} + +// =========================================================================== +// CIccTagJsonFixedNum template +// =========================================================================== + +template +const char* CIccTagJsonFixedNum::GetClassName() const +{ + if (Tsig == icSigS15Fixed16ArrayType) return "CIccTagJsonS15Fixed16"; + return "CIccTagJsonU16Fixed16"; +} + +template +bool CIccTagJsonFixedNum::ToJson(IccJson &j) +{ + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < this->m_nSize; i++) + arr.push_back(icFtoD(this->m_Num[i])); + j["values"] = arr; + return true; +} + +template +bool CIccTagJsonFixedNum::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (jsonExistsField(j, "values") && j["values"].is_array()) { + const IccJson &arr = j["values"]; + if (!this->SetSize((icUInt32Number)arr.size())) return false; + for (size_t i = 0; i < arr.size(); i++) + this->m_Num[i] = icDtoF(arr[i].get()); + } + return true; +} + +template class CIccTagJsonFixedNum; + +// =========================================================================== +// CIccTagJsonNum template +// =========================================================================== + +template +const char* CIccTagJsonNum::GetClassName() const +{ + if (Tsig == icSigUInt8ArrayType) return "CIccTagJsonUInt8"; + if (Tsig == icSigUInt16ArrayType) return "CIccTagJsonUInt16"; + if (Tsig == icSigUInt32ArrayType) return "CIccTagJsonUInt32"; + return "CIccTagJsonUInt64"; +} + +template +bool CIccTagJsonNum::ToJson(IccJson &j) +{ + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < this->m_nSize; i++) + arr.push_back(this->m_Num[i]); + j["values"] = arr; + return true; +} + +template +bool CIccTagJsonNum::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (jsonExistsField(j, "values") && j["values"].is_array()) { + const IccJson &arr = j["values"]; + if (!this->SetSize((icUInt32Number)arr.size())) return false; + for (size_t i = 0; i < arr.size(); i++) + this->m_Num[i] = arr[i].get(); + } + return true; +} + +template class CIccTagJsonNum, icSigUInt8ArrayType>; +template class CIccTagJsonNum, icSigUInt16ArrayType>; +template class CIccTagJsonNum, icSigUInt32ArrayType>; +template class CIccTagJsonNum, icSigUInt64ArrayType>; + +// =========================================================================== +// CIccTagJsonFloatNum template +// =========================================================================== + +template +const char* CIccTagJsonFloatNum::GetClassName() const +{ + if (Tsig == icSigFloat16ArrayType) return "CIccTagJsonFloat16"; + if (Tsig == icSigFloat32ArrayType) return "CIccTagJsonFloat32"; + return "CIccTagJsonFloat64"; +} + +template +bool CIccTagJsonFloatNum::ToJson(IccJson &j) +{ + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < this->m_nSize; i++) + arr.push_back((double)this->m_Num[i]); + j["values"] = arr; + return true; +} + +template +bool CIccTagJsonFloatNum::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (jsonExistsField(j, "values") && j["values"].is_array()) { + const IccJson &arr = j["values"]; + if (!this->SetSize((icUInt32Number)arr.size())) return false; + for (size_t i = 0; i < arr.size(); i++) + this->m_Num[i] = (T)arr[i].get(); + } + return true; +} + +template class CIccTagJsonFloatNum, icSigFloat16ArrayType>; +template class CIccTagJsonFloatNum, icSigFloat32ArrayType>; +template class CIccTagJsonFloatNum, icSigFloat64ArrayType>; + +// =========================================================================== +// CIccTagJsonMultiLocalizedUnicode +// =========================================================================== + +bool CIccTagJsonMultiLocalizedUnicode::ToJson(IccJson &j) +{ + IccJson locales = IccJson::array(); + if (m_Strings) { + CIccMultiLocalizedUnicode::iterator i; + for (i = m_Strings->begin(); i != m_Strings->end(); i++) { + IccJson loc; + char lang[3] = {0}, country[3] = {0}; + lang[0] = (char)(i->m_nLanguageCode >> 8); + lang[1] = (char)(i->m_nLanguageCode & 0xFF); + country[0] = (char)(i->m_nCountryCode >> 8); + country[1] = (char)(i->m_nCountryCode & 0xFF); + loc["language"] = lang; + loc["country"] = country; + std::string text; + if (i->GetBuf() && i->GetLength() > 0) + icUtf16ToUtf8(text, i->GetBuf(), (int)i->GetLength()); + loc["text"] = text; + locales.push_back(loc); + } + } + j["localizedStrings"] = locales; + return true; +} + +bool CIccTagJsonMultiLocalizedUnicode::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + if (!jsonExistsField(j, "localizedStrings") || !j["localizedStrings"].is_array()) + return false; + for (const auto &loc : j["localizedStrings"]) { + std::string lang = " ", country = " ", text; + jGetString(loc, "language", lang); + jGetString(loc, "country", country); + jGetString(loc, "text", text); + + while (lang.size() < 2) lang += ' '; + while (country.size() < 2) country += ' '; + + icLanguageCode langCode = (icLanguageCode) ((lang[0] << 8) | lang[1]); + icCountryCode countryCode= (icCountryCode) ((country[0] << 8) | country[1]); + + CIccUTF16String wstr(text.c_str()); + SetText(wstr.c_str(), langCode, countryCode); + } + return true; +} + +// =========================================================================== +// CIccTagJsonTagData +// =========================================================================== + +bool CIccTagJsonTagData::ToJson(IccJson &j) +{ + j["dataFlag"] = (int)m_nDataFlag; + j["data"] = icJsonDumpHexData(m_pData, m_nSize); + return true; +} + +bool CIccTagJsonTagData::ParseJson(const IccJson &j, std::string & /*parseStr*/) +{ + int dataFlag = 0; + if (jGetValue(j, "dataFlag", dataFlag)) + m_nDataFlag = (icDataBlockType)dataFlag; + std::string hex; + if (jGetString(j, "data", hex)) { + icUInt32Number sz = icJsonGetHexDataSize(hex.c_str()); + if (!SetSize(sz)) return false; + icJsonGetHexData(m_pData, hex.c_str(), sz); + } + return true; +} + +// =========================================================================== +// CIccTagJsonProfileSeqDesc helpers +// =========================================================================== + +static bool icProfDescToJson(IccJson &jDesc, const CIccProfileDescStruct &p) +{ + const size_t bufSize = 16; + char buf[bufSize]; + + jDesc["deviceManufacturerSignature"] = icGetSigStr(buf, bufSize, p.m_deviceMfg); + jDesc["deviceModelSignature"] = icGetSigStr(buf, bufSize, p.m_deviceModel); + jDesc["deviceAttributes"] = icJsonGetDeviceAttr(p.m_attributes); + jDesc["technology"] = icGetSigStr(buf, bufSize, p.m_technology); + + auto serializeDescText = [](IccJson &jField, const CIccProfileDescText &descText) -> bool { + CIccTag *pTag = descText.GetTag(); + if (!pTag) + return true; // optional + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) + return false; + CIccTagJson *pJsonTag = static_cast(pExt); + IccJson tagData; + if (!pJsonTag->ToJson(tagData)) + return false; + const icChar *typeName = CIccTagCreator::GetTagTypeSigName(pTag->GetType()); + if (!typeName) typeName = "PrivateType"; + jField["type"] = typeName; + jField.update(tagData); + return true; + }; + + IccJson jMfgDesc, jModDesc; + if (!serializeDescText(jMfgDesc, p.m_deviceMfgDesc)) return false; + if (!serializeDescText(jModDesc, p.m_deviceModelDesc)) return false; + if (!jMfgDesc.empty()) jDesc["deviceManufacturer"] = jMfgDesc; + if (!jModDesc.empty()) jDesc["deviceModel"] = jModDesc; + + return true; +} + +static bool icJsonParseProfDesc(const IccJson &jDesc, CIccProfileDescStruct &p, std::string &parseStr) +{ + std::string sig; + jGetString(jDesc, "deviceManufacturerSignature", sig); + p.m_deviceMfg = sig.empty() ? 0 : (icSignature)icGetSigVal(sig.c_str()); + + sig.clear(); + jGetString(jDesc, "deviceModelSignature", sig); + p.m_deviceModel = sig.empty() ? 0 : (icSignature)icGetSigVal(sig.c_str()); + + if (jsonExistsField(jDesc, "deviceAttributes") && jDesc["deviceAttributes"].is_object()) + p.m_attributes = icJsonParseDeviceAttr(jDesc["deviceAttributes"]); + else + p.m_attributes = 0; + + sig.clear(); + jGetString(jDesc, "technology", sig); + p.m_technology = sig.empty() ? (icTechnologySignature)0 + : (icTechnologySignature)icGetSigVal(sig.c_str()); + + auto parseDescText = [&parseStr](const IccJson &jField, CIccProfileDescText &descText) -> bool { + if (!jField.is_object() || jField.empty()) + return true; + std::string typeName; + jGetString(jField, "type", typeName); + icTagTypeSignature typeSig = CIccTagCreator::GetTagTypeNameSig(typeName.c_str()); + if (typeSig == icSigUnknownType) + return false; + if (!descText.SetType(typeSig)) + return false; + CIccTag *pTag = descText.GetTag(); + if (!pTag) + return false; + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) + return false; + return static_cast(pExt)->ParseJson(jField, parseStr); + }; + + if (jsonExistsField(jDesc, "deviceManufacturer") && jDesc["deviceManufacturer"].is_object()) + if (!parseDescText(jDesc["deviceManufacturer"], p.m_deviceMfgDesc)) return false; + + if (jsonExistsField(jDesc, "deviceModel") && jDesc["deviceModel"].is_object()) + if (!parseDescText(jDesc["deviceModel"], p.m_deviceModelDesc)) return false; + + return true; +} + +// =========================================================================== +// CIccTagJsonProfileSeqDesc +// =========================================================================== + +bool CIccTagJsonProfileSeqDesc::ToJson(IccJson &j) +{ + if (!m_Descriptions) + return false; + + IccJson seq = IccJson::array(); + for (auto &desc : *m_Descriptions) { + IccJson jDesc; + if (!icProfDescToJson(jDesc, desc)) + return false; + seq.push_back(jDesc); + } + j["profileSequence"] = seq; + return true; +} + +bool CIccTagJsonProfileSeqDesc::ParseJson(const IccJson &j, std::string &parseStr) +{ + if (!m_Descriptions) + return false; + + m_Descriptions->clear(); + + if (!jsonExistsField(j, "profileSequence") || !j["profileSequence"].is_array()) + return true; // empty sequence is valid + + for (const IccJson &jDesc : j["profileSequence"]) { + CIccProfileDescStruct desc; + if (!icJsonParseProfDesc(jDesc, desc, parseStr)) + return false; + m_Descriptions->push_back(desc); + } + return true; +} + +// =========================================================================== +// CIccTagJsonProfileSequenceId +// =========================================================================== + +bool CIccTagJsonProfileSequenceId::ToJson(IccJson &j) +{ + if (!m_list) + return false; + + IccJson seq = IccJson::array(); + for (auto &pid : *m_list) { + IccJson entry; + + // 16-byte profile ID serialized as 32-character uppercase hex string + char buf[33]; + for (int n = 0; n < 16; n++) + snprintf(buf + n * 2, 33 - n * 2, "%02X", pid.m_profileID.ID8[n]); + buf[32] = '\0'; + entry["profileId"] = buf; + + entry["localizedStrings"] = dictLocalizedToJson(&pid.m_desc); + + seq.push_back(entry); + } + j["profileSequenceId"] = seq; + return true; +} + +bool CIccTagJsonProfileSequenceId::ParseJson(const IccJson &j, std::string &parseStr) +{ + if (!m_list) + return false; + + m_list->clear(); + + if (!jsonExistsField(j, "profileSequenceId") || !j["profileSequenceId"].is_array()) + return true; + + for (const IccJson &entry : j["profileSequenceId"]) { + CIccProfileIdDesc desc; + + std::string idHex; + if (jGetString(entry, "profileId", idHex) && !idHex.empty()) + icJsonGetHexData(&desc.m_profileID, idHex.c_str(), sizeof(desc.m_profileID)); + + if (jsonExistsField(entry, "localizedStrings") && entry["localizedStrings"].is_array()) { + for (const IccJson &loc : entry["localizedStrings"]) { + std::string lang = " ", country = " ", text; + jGetString(loc, "language", lang); + jGetString(loc, "country", country); + jGetString(loc, "text", text); + + while (lang.size() < 2) lang += ' '; + while (country.size() < 2) country += ' '; + + icLanguageCode langCode = (icLanguageCode)((lang[0] << 8) | lang[1]); + icCountryCode countryCode = (icCountryCode) ((country[0] << 8) | country[1]); + + CIccUTF16String wstr(text.c_str()); + desc.m_desc.SetText(wstr.c_str(), langCode, countryCode); + } + } + + m_list->push_back(desc); + } + return true; +} + +// =========================================================================== +// CIccTagJsonResponseCurveSet16 +// =========================================================================== + +bool CIccTagJsonResponseCurveSet16::ToJson(IccJson &j) +{ + CIccInfo info; + j["CountOfChannels"] = (int)m_nChannels; + + IccJson curves = IccJson::array(); + CIccResponseCurveStruct *pCurve = GetFirstCurves(); + while (pCurve) { + IccJson jCurve; + jCurve["MeasurementUnit"] = info.GetMeasurementUnit((icSignature)pCurve->GetMeasurementType()); + + IccJson channels = IccJson::array(); + for (icUInt16Number i = 0; i < pCurve->GetNumChannels(); i++) { + IccJson jChan; + icXYZNumber *pXYZ = pCurve->GetXYZ(i); + jChan["MaxColorantXYZ"] = IccJson::array({ icFtoD(pXYZ->X), icFtoD(pXYZ->Y), icFtoD(pXYZ->Z) }); + + IccJson measurements = IccJson::array(); + CIccResponse16List *pList = pCurve->GetResponseList(i); + for (auto m = pList->begin(); m != pList->end(); ++m) { + IccJson jm; + jm["DeviceCode"] = (int)m->deviceCode; + jm["MeasValue"] = icFtoD(m->measurementValue); + if (m->reserved) + jm["Reserved"] = (int)m->reserved; + measurements.push_back(jm); + } + jChan["Measurements"] = measurements; + channels.push_back(jChan); + } + jCurve["Channels"] = channels; + curves.push_back(jCurve); + pCurve = GetNextCurves(); + } + j["ResponseCurves"] = curves; + return true; +} + +bool CIccTagJsonResponseCurveSet16::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nChannels = 0; + if (!jGetValue(j, "CountOfChannels", nChannels) || nChannels <= 0) { + parseStr += "Missing or invalid CountOfChannels in ResponseCurveSet16\n"; + return false; + } + SetNumChannels((icUInt16Number)nChannels); + + if (!m_ResponseCurves) + return false; + m_ResponseCurves->clear(); + + if (!jsonExistsField(j, "ResponseCurves") || !j["ResponseCurves"].is_array()) + return true; + + for (const IccJson &jCurve : j["ResponseCurves"]) { + std::string measUnit; + jGetString(jCurve, "MeasurementUnit", measUnit); + icMeasurementUnitSig sig = measUnit.empty() ? icSigStatusA + : icGetMeasurementValue(measUnit.c_str()); + + CIccResponseCurveStruct curves(sig, (icUInt16Number)nChannels); + + if (!jsonExistsField(jCurve, "Channels") || !jCurve["Channels"].is_array()) { + parseStr += "Missing Channels in ResponseCurve entry\n"; + return false; + } + + int i = 0; + for (const IccJson &jChan : jCurve["Channels"]) { + if (i >= nChannels) break; + + icXYZNumber *pXYZ = curves.GetXYZ(i); + pXYZ->X = pXYZ->Y = pXYZ->Z = 0; + if (jsonExistsField(jChan, "MaxColorantXYZ") && jChan["MaxColorantXYZ"].is_array() + && jChan["MaxColorantXYZ"].size() >= 3) { + pXYZ->X = icDtoF((icFloatNumber)jChan["MaxColorantXYZ"][0].get()); + pXYZ->Y = icDtoF((icFloatNumber)jChan["MaxColorantXYZ"][1].get()); + pXYZ->Z = icDtoF((icFloatNumber)jChan["MaxColorantXYZ"][2].get()); + } + + CIccResponse16List *pList = curves.GetResponseList(i); + if (jsonExistsField(jChan, "Measurements") && jChan["Measurements"].is_array()) { + for (const IccJson &jm : jChan["Measurements"]) { + icResponse16Number resp = {}; + int dc = 0, res = 0; + double mv = 0.0; + jGetValue(jm, "DeviceCode", dc); + jGetValue(jm, "MeasValue", mv); + jGetValue(jm, "Reserved", res); + resp.deviceCode = (icUInt16Number)dc; + resp.measurementValue = icDtoF((icFloatNumber)mv); + resp.reserved = (icUInt16Number)res; + pList->push_back(resp); + } + } + i++; + } + m_ResponseCurves->push_back(curves); + } + return true; +} + +// =========================================================================== +// Curve tags +// =========================================================================== + +bool CIccTagJsonCurve::ToJson(IccJson &j) +{ + return ToJson(j, icConvertFloat); +} + +bool CIccTagJsonCurve::ToJson(IccJson &j, icConvertType nType) +{ + if (m_nSize == 0) { + j["curveType"] = "identity"; + } else if (m_nSize == 1) { + j["curveType"] = "gamma"; + j["gamma"] = (double)m_Curve[0]; + } else { + j["curveType"] = "table"; + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < m_nSize; i++) { + switch (nType) { + case icConvert8Bit: + arr.push_back((int)(m_Curve[i] * 255.0f + 0.5f)); break; + case icConvert16Bit: + case icConvertVariable: + arr.push_back((int)(m_Curve[i] * 65535.0f + 0.5f)); break; + default: + arr.push_back((double)m_Curve[i]); break; + } + } + if (nType == icConvert8Bit) + j["precision"] = 1; + else if (nType == icConvert16Bit || nType == icConvertVariable) + j["precision"] = 2; + j["table"] = arr; + } + return true; +} + +bool CIccTagJsonCurve::ParseJson(const IccJson &j, std::string &parseStr) +{ + return ParseJson(j, icConvertFloat, parseStr); +} + +bool CIccTagJsonCurve::ParseJson(const IccJson &j, icConvertType /*nType*/, std::string & /*parseStr*/) +{ + std::string curveType; + if (!jGetString(j, "curveType", curveType)) return false; + if (curveType == "identity") { + SetSize(0); + } else if (curveType == "gamma") { + SetSize(1); + double gamma = 1.0; + jGetValue(j, "gamma", gamma); + m_Curve[0] = (icFloatNumber)gamma; + } else { + if (!jsonExistsField(j, "table") || !j["table"].is_array()) return false; + const IccJson &arr = j["table"]; + if (!SetSize((icUInt32Number)arr.size())) return false; + int precision = 0; + jGetValue(j, "precision", precision); + for (size_t i = 0; i < arr.size(); i++) { + if (precision == 1) + m_Curve[i] = (icFloatNumber)arr[i].get() / 255.0f; + else if (precision == 2) + m_Curve[i] = (icFloatNumber)arr[i].get() / 65535.0f; + else + m_Curve[i] = (icFloatNumber)arr[i].get(); + } + } + return true; +} + +// --------------------------------------------------------------------------- + +bool CIccTagJsonParametricCurve::ToJson(IccJson &j) +{ + return ToJson(j, icConvertFloat); +} + +bool CIccTagJsonParametricCurve::ToJson(IccJson &j, icConvertType /*nType*/) +{ + j["functionType"] = (int)m_nFunctionType; + IccJson params = IccJson::array(); + for (icUInt8Number i = 0; i < m_nNumParam; i++) + params.push_back(icFtoD(m_dParam[i])); + j["params"] = params; + return true; +} + +bool CIccTagJsonParametricCurve::ParseJson(const IccJson &j, std::string &parseStr) +{ + return ParseJson(j, icConvertFloat, parseStr); +} + +bool CIccTagJsonParametricCurve::ParseJson(const IccJson &j, icConvertType /*nType*/, std::string & /*parseStr*/) +{ + int funcType = 0; + if (!jGetValue(j, "functionType", funcType)) return false; + icUInt8Number nParam = 0; + // Determine param count from function type per ICC spec + switch (funcType) { case 0: nParam=1; break; case 1: nParam=3; break; case 2: nParam=4; break; case 3: nParam=5; break; case 4: nParam=7; break; default: nParam=0; } + if (!SetFunctionType((icUInt16Number)funcType)) return false; + if (jsonExistsField(j, "params") && j["params"].is_array()) { + const IccJson ¶ms = j["params"]; + for (icUInt8Number i = 0; i < nParam && i < (icUInt8Number)params.size(); i++) + m_dParam[i] = icDtoF(params[i].get()); + } + return true; +} + +// --------------------------------------------------------------------------- + +bool CIccTagJsonSegmentedCurve::ToJson(IccJson &j) +{ + return ToJson(j, icConvertFloat); +} + +bool CIccTagJsonSegmentedCurve::ToJson(IccJson &j, icConvertType /*nType*/) +{ + if (!m_pCurve) + return true; + CIccSegmentedCurveJson jsonCurve(m_pCurve); + return jsonCurve.ToJson(j); +} + +bool CIccTagJsonSegmentedCurve::ParseJson(const IccJson &j, std::string &parseStr) +{ + return ParseJson(j, icConvertFloat, parseStr); +} + +bool CIccTagJsonSegmentedCurve::ParseJson(const IccJson &j, icConvertType /*nType*/, std::string &parseStr) +{ + CIccSegmentedCurveJson *pCurve = new CIccSegmentedCurveJson(); + if (!pCurve->ParseJson(j, parseStr)) { + delete pCurve; + return false; + } + SetCurve(pCurve); + return true; +} + +// =========================================================================== +// MBB (LUT tag) JSON helpers +// =========================================================================== + +// Curve type name for JSON "type" discriminator +static const char *icJsonCurveTypeName(icTagTypeSignature sig) +{ + switch (sig) { + case icSigCurveType: return "Curve"; + case icSigParametricCurveType: return "ParametricCurve"; + case icSigSegmentedCurveType: return "SegmentedCurve"; + default: return nullptr; + } +} + +// Serialize a curve array into a JSON array +static bool icMBBCurvesToJson(IccJson &arr, LPIccCurve *pCurves, int nChannels, icConvertType nType) +{ + arr = IccJson::array(); + for (int i = 0; i < nChannels; i++) { + CIccCurve *pCurve = pCurves[i]; + if (!pCurve) return false; + IIccExtensionTag *pExt = pCurve->GetExtension(); + if (!pExt || strcmp(pExt->GetExtDerivedClassName(), "CIccCurveJson") != 0) + return false; + CIccCurveJson *pCurveJson = (CIccCurveJson *)pExt; + IccJson jCurve; + const char *typeName = icJsonCurveTypeName(pCurve->GetType()); + if (!typeName) return false; + jCurve["type"] = typeName; + if (!pCurveJson->ToJson(jCurve, nType)) + return false; + arr.push_back(jCurve); + } + return true; +} + +// Deserialize a JSON array into a curve array +static bool icMBBCurvesFromJson(LPIccCurve *pCurves, int nChannels, + const IccJson &arr, icConvertType nType, std::string &parseStr) +{ + if (!arr.is_array() || (int)arr.size() != nChannels) { + parseStr += "Curve array length does not match channel count\n"; + return false; + } + for (int i = 0; i < nChannels; i++) { + const IccJson &jCurve = arr[i]; + std::string type; + jGetString(jCurve, "type", type); + + CIccCurve *pTag = nullptr; + CIccCurveJson *pCurveJson = nullptr; + if (type == "Curve") { + auto *p = new CIccTagJsonCurve(); pTag = p; pCurveJson = p; + } else if (type == "ParametricCurve") { + auto *p = new CIccTagJsonParametricCurve(); pTag = p; pCurveJson = p; + } else if (type == "SegmentedCurve") { + auto *p = new CIccTagJsonSegmentedCurve(); pTag = p; pCurveJson = p; + } else { + parseStr += "Unknown curve type: " + type + "\n"; + return false; + } + if (!pCurveJson->ParseJson(jCurve, nType, parseStr)) { + parseStr += "Failed to parse curve " + std::to_string(i) + "\n"; + delete pTag; + return false; + } + pCurves[i] = pTag; + } + return true; +} + +// Serialize a CIccMatrix into j["matrix"] +static void icMBBMatrixToJson(IccJson &j, CIccMatrix *pMatrix) +{ + IccJson e = IccJson::array(); + int n = pMatrix->m_bUseConstants ? 12 : 9; + for (int i = 0; i < n; i++) + e.push_back((double)pMatrix->m_e[i]); + j["matrix"]["e"] = e; +} + +// Deserialize j["matrix"]["e"] into a CIccMatrix +static bool icMBBMatrixFromJson(CIccMatrix *pMatrix, const IccJson &j) +{ + memset(pMatrix->m_e, 0, sizeof(pMatrix->m_e)); + pMatrix->m_bUseConstants = false; + if (!jsonExistsField(j, "e") || !j["e"].is_array()) return false; + const IccJson &e = j["e"]; + int n = std::min((int)e.size(), 12); + for (int i = 0; i < n; i++) + pMatrix->m_e[i] = (icFloatNumber)e[i].get(); + if (n > 9) + pMatrix->m_bUseConstants = true; + return true; +} + +// Parse whitespace/comma-separated numbers from a text buffer into a CLUT. +// Returns the number of values read. +static icUInt32Number icParseCLUTTextBuf(const char *buf, size_t bufLen, + icFloatNumber *dst, icUInt32Number maxVals, + icConvertType nType) +{ + const char *p = buf; + const char *end = buf + bufLen; + icUInt32Number n = 0; + while (p < end && n < maxVals) { + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',')) + p++; + if (p >= end) break; + char *next = nullptr; + double v = strtod(p, &next); + if (!next || next == p) break; + switch (nType) { + case icConvert8Bit: dst[n++] = (icFloatNumber)(v / 255.0); break; + case icConvert16Bit: dst[n++] = (icFloatNumber)(v / 65535.0); break; + default: dst[n++] = (icFloatNumber)v; break; + } + p = next; + } + return n; +} + +// Construct and initialize a CIccCLUT from a JSON object. +// Supports inline "data" array or external "file" (text or binary). +// +// External file JSON keys: +// "file" : path to data file (triggers file-based loading) +// "format" : "text" (default) or "binary" +// "encoding" : "int8", "int16", or "float" (default: matches nType) +// "endian" : "little" or "big" (binary only; default "big") +CIccCLUT *icCLUTFromJson(const IccJson &j, int nIn, int nOut, + icConvertType nType, std::string &parseStr) +{ + // Resolve variable type from stored precision field (mirrors icConvertVariable in XML) + if (nType == icConvertVariable) { + int prec = 2; + jGetValue(j, "precision", prec); + nType = (prec == 1) ? icConvert8Bit : icConvert16Bit; + } + int nPrecision = (nType == icConvert8Bit) ? 1 : 2; + CIccCLUT *pCLUT = new CIccCLUT((icUInt8Number)nIn, (icUInt16Number)nOut, nPrecision); + if (!pCLUT) return nullptr; + + // Initialize grid dimensions + if (jsonExistsField(j, "gridPoints") && j["gridPoints"].is_array()) { + const IccJson &gp = j["gridPoints"]; + icUInt8Number pts[16] = {}; + int cnt = std::min((int)gp.size(), nIn); + for (int i = 0; i < cnt; i++) + pts[i] = (icUInt8Number)gp[i].get(); + if (!pCLUT->Init(pts)) { + parseStr += "Failed to initialize CLUT from gridPoints array\n"; + delete pCLUT; return nullptr; + } + } else if (jsonExistsField(j, "gridPoints") && j["gridPoints"].is_number()) { + icUInt8Number gran = (icUInt8Number)j["gridPoints"].get(); + if (!pCLUT->Init(gran)) { + parseStr += "Failed to initialize CLUT from gridPoints scalar\n"; + delete pCLUT; return nullptr; + } + } else { + parseStr += "Missing gridPoints in CLUT\n"; + delete pCLUT; return nullptr; + } + + icUInt32Number nTotal = pCLUT->NumPoints() * (icUInt32Number)nOut; + icFloatNumber *pData = pCLUT->GetData(0); + if (!pData) { delete pCLUT; return nullptr; } + + // ----------------------------------------------------------------------- + // External file + // ----------------------------------------------------------------------- + if (jsonExistsField(j, "file") && j["file"].is_string()) { + std::string filename = j["file"].get(); + std::string format = "text"; + std::string encoding; + std::string endian = "big"; + jGetString(j, "format", format); + jGetString(j, "encoding", encoding); + jGetString(j, "endian", endian); + + // Resolve encoding → effective convert type + icConvertType fileType = nType; + if (encoding == "int8") fileType = icConvert8Bit; + else if (encoding == "int16") fileType = icConvert16Bit; + else if (encoding == "float") fileType = icConvertFloat; + + CIccIO *file = IccJsonOpenFileIO(filename.c_str(), "rb"); + if (!file) { + parseStr += "Error! - File '" + filename + "' not found.\n"; + delete pCLUT; return nullptr; + } + + bool ok = false; + + if (format == "text") { + size_t num = file->GetLength(); + char *buf = (char *)malloc(num + 1); + if (!buf) { + parseStr += "Memory error reading '" + filename + "'\n"; + delete file; delete pCLUT; return nullptr; + } + if (file->Read8(buf, num) != num) { + parseStr += "Read error on '" + filename + "'\n"; + free(buf); delete file; delete pCLUT; return nullptr; + } + buf[num] = '\0'; + icUInt32Number n = icParseCLUTTextBuf(buf, num, pData, nTotal, fileType); + free(buf); + if (n != nTotal) { + parseStr += "Error! - Entry count in '" + filename + "' does not match CLUT size.\n"; + delete file; delete pCLUT; return nullptr; + } + ok = true; + } + else if (format == "binary") { + bool little_endian = (endian == "little"); + + if (fileType == icConvert8Bit) { + size_t num = file->GetLength(); + if (num != nTotal) { + parseStr += "Error! - Binary file '" + filename + "' size does not match CLUT.\n"; + delete file; delete pCLUT; return nullptr; + } + icUInt8Number v; + for (icUInt32Number k = 0; k < nTotal; k++) { + if (!file->Read8(&v)) { + parseStr += "Read error on '" + filename + "'\n"; + delete file; delete pCLUT; return nullptr; + } + pData[k] = (icFloatNumber)v / 255.0f; + } + ok = true; + } + else if (fileType == icConvert16Bit) { + size_t num = file->GetLength() / sizeof(icUInt16Number); + if (num < nTotal) { + parseStr += "Error! - Binary file '" + filename + "' too small for CLUT.\n"; + delete file; delete pCLUT; return nullptr; + } + icUInt16Number v; + icUInt8Number *ptr = (icUInt8Number *)&v; + for (icUInt32Number k = 0; k < nTotal; k++) { + if (!file->Read16(&v)) { + parseStr += "Read error on '" + filename + "'\n"; + delete file; delete pCLUT; return nullptr; + } +#ifdef ICC_BYTE_ORDER_LITTLE_ENDIAN + if (little_endian) { +#else + if (!little_endian) { +#endif + icUInt8Number t = ptr[0]; ptr[0] = ptr[1]; ptr[1] = t; + } + pData[k] = (icFloatNumber)v / 65535.0f; + } + ok = true; + } + else { // float32 + size_t num = file->GetLength() / sizeof(icFloat32Number); + if (num < nTotal) { + parseStr += "Error! - Binary file '" + filename + "' too small for CLUT.\n"; + delete file; delete pCLUT; return nullptr; + } + icFloat32Number v; + icUInt8Number *ptr = (icUInt8Number *)&v; + for (icUInt32Number k = 0; k < nTotal; k++) { + if (!file->ReadFloat32Float(&v)) { + parseStr += "Read error on '" + filename + "'\n"; + delete file; delete pCLUT; return nullptr; + } +#ifdef ICC_BYTE_ORDER_LITTLE_ENDIAN + if (little_endian) { +#else + if (!little_endian) { +#endif + icUInt8Number tmp; + tmp = ptr[0]; ptr[0] = ptr[3]; ptr[3] = tmp; + tmp = ptr[1]; ptr[1] = ptr[2]; ptr[2] = tmp; + } + pData[k] = v; + } + ok = true; + } + } + else { + parseStr += "Unknown CLUT file format '" + format + "' (expected 'text' or 'binary')\n"; + } + + delete file; + if (!ok) { delete pCLUT; return nullptr; } + return pCLUT; + } + + // ----------------------------------------------------------------------- + // Inline data array + // ----------------------------------------------------------------------- + if (!jsonExistsField(j, "data") || !j["data"].is_array()) { + parseStr += "Missing 'data' or 'file' in CLUT\n"; + delete pCLUT; return nullptr; + } + const IccJson &data = j["data"]; + for (icUInt32Number k = 0; k < nTotal && k < (icUInt32Number)data.size(); k++) { + double v = data[k].get(); + switch (nType) { + case icConvert8Bit: pData[k] = (icFloatNumber)(v / 255.0); break; + case icConvert16Bit: pData[k] = (icFloatNumber)(v / 65535.0); break; + default: pData[k] = (icFloatNumber)v; break; + } + } + return pCLUT; +} + +// Serialize an MBB to JSON +static bool icMBBToJson(IccJson &j, CIccMBB *pMBB, icConvertType nType, bool bSaveGridPoints) +{ + j["inputChannels"] = (int)pMBB->InputChannels(); + j["outputChannels"] = (int)pMBB->OutputChannels(); + + int nIn = pMBB->InputChannels(); + int nOut = pMBB->OutputChannels(); + + auto emitCurves = [&](const char *name, LPIccCurve *pCurves, int n) -> bool { + IccJson arr; + if (!icMBBCurvesToJson(arr, pCurves, n, nType)) return false; + j[name] = arr; + return true; + }; + + if (pMBB->IsInputMatrix()) { + if (pMBB->SwapMBCurves()) { + if (pMBB->GetMatrix()) icMBBMatrixToJson(j, pMBB->GetMatrix()); + if (pMBB->GetCurvesB() && !emitCurves("bCurves", pMBB->GetCurvesM(), nIn)) return false; + } else { + if (pMBB->GetCurvesB() && !emitCurves("bCurves", pMBB->GetCurvesB(), nIn)) return false; + if (pMBB->GetMatrix()) icMBBMatrixToJson(j, pMBB->GetMatrix()); + if (pMBB->GetCurvesM() && !emitCurves("mCurves", pMBB->GetCurvesM(), nIn)) return false; + } + if (pMBB->GetCLUT() && !icCLUTToJson(j, pMBB->GetCLUT(), nType, bSaveGridPoints, "clut")) return false; + if (pMBB->GetCurvesA() && !emitCurves("aCurves", pMBB->GetCurvesA(), nOut)) return false; + } else { + if (pMBB->GetCurvesA() && !emitCurves("aCurves", pMBB->GetCurvesA(), nIn)) return false; + if (pMBB->GetCLUT() && !icCLUTToJson(j, pMBB->GetCLUT(), nType, bSaveGridPoints, "clut")) return false; + if (pMBB->GetCurvesM() && !emitCurves("mCurves", pMBB->GetCurvesM(), nOut)) return false; + if (pMBB->GetMatrix()) icMBBMatrixToJson(j, pMBB->GetMatrix()); + if (pMBB->GetCurvesB() && !emitCurves("bCurves", pMBB->GetCurvesB(), nOut)) return false; + } + return true; +} + +// Deserialize an MBB from JSON +static bool icMBBFromJson(CIccMBB *pMBB, const IccJson &j, icConvertType nType, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + if (!jGetValue(j, "inputChannels", nIn) || !jGetValue(j, "outputChannels", nOut) + || nIn < 1 || nOut < 1 || nIn > 15 || nOut > 15) { + parseStr += "Missing or invalid inputChannels/outputChannels in LUT\n"; + return false; + } + pMBB->Init((icUInt8Number)nIn, (icUInt8Number)nOut); + + // aCurves + if (jsonExistsField(j, "aCurves") && j["aCurves"].is_array()) { + int nCh = !pMBB->IsInputMatrix() ? nIn : nOut; + if (!icMBBCurvesFromJson(pMBB->NewCurvesA(), nCh, j["aCurves"], nType, parseStr)) { + parseStr += "Error parsing aCurves\n"; return false; + } + } + // bCurves + if (jsonExistsField(j, "bCurves") && j["bCurves"].is_array()) { + int nCh = pMBB->IsInputMatrix() ? nIn : nOut; + if (!icMBBCurvesFromJson(pMBB->NewCurvesB(), nCh, j["bCurves"], nType, parseStr)) { + parseStr += "Error parsing bCurves\n"; return false; + } + } + // mCurves + if (jsonExistsField(j, "mCurves") && j["mCurves"].is_array()) { + int nCh = pMBB->IsInputMatrix() ? nIn : nOut; + if (!icMBBCurvesFromJson(pMBB->NewCurvesM(), nCh, j["mCurves"], nType, parseStr)) { + parseStr += "Error parsing mCurves\n"; return false; + } + } + // matrix + if (jsonExistsField(j, "matrix") && j["matrix"].is_object()) { + if (!icMBBMatrixFromJson(pMBB->NewMatrix(), j["matrix"])) { + parseStr += "Error parsing matrix\n"; return false; + } + } + // clut + if (jsonExistsField(j, "clut") && j["clut"].is_object()) { + CIccCLUT *pCLUT = icCLUTFromJson(j["clut"], nIn, nOut, nType, parseStr); + if (!pCLUT || !pMBB->SetCLUT(pCLUT)) { + delete pCLUT; + parseStr += "Error parsing clut\n"; return false; + } + } + return true; +} + +// =========================================================================== +// LUT tags +// =========================================================================== + +bool CIccTagJsonLutAtoB::ToJson(IccJson &j) +{ + return icMBBToJson(j, this, icConvertVariable, true); +} +bool CIccTagJsonLutAtoB::ParseJson(const IccJson &j, std::string &parseStr) +{ + return icMBBFromJson(this, j, icConvertVariable, parseStr); +} + +bool CIccTagJsonLutBtoA::ToJson(IccJson &j) +{ + return icMBBToJson(j, this, icConvertVariable, true); +} +bool CIccTagJsonLutBtoA::ParseJson(const IccJson &j, std::string &parseStr) +{ + return icMBBFromJson(this, j, icConvertVariable, parseStr); +} + +bool CIccTagJsonLut8::ToJson(IccJson &j) +{ + return icMBBToJson(j, this, icConvert8Bit, true); +} +bool CIccTagJsonLut8::ParseJson(const IccJson &j, std::string &parseStr) +{ + return icMBBFromJson(this, j, icConvert8Bit, parseStr); +} + +bool CIccTagJsonLut16::ToJson(IccJson &j) +{ + return icMBBToJson(j, this, icConvert16Bit, true); +} +bool CIccTagJsonLut16::ParseJson(const IccJson &j, std::string &parseStr) +{ + return icMBBFromJson(this, j, icConvert16Bit, parseStr); +} + +// =========================================================================== +// CIccTagJsonMultiProcessElement +// =========================================================================== + +bool CIccTagJsonMultiProcessElement::ToJson(IccJson &j) +{ + j["inputChannels"] = (int)m_nInputChannels; + j["outputChannels"] = (int)m_nOutputChannels; + + IccJson elems = IccJson::array(); + if (m_list) { + CIccMultiProcessElementList::iterator it; + for (it = m_list->begin(); it != m_list->end(); it++) { + CIccMultiProcessElement *pElem = it->ptr; + IIccExtensionMpe *pExt = pElem ? pElem->GetExtension() : NULL; + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccMpeJson") != 0) + continue; + CIccMpeJson *pJsonElem = static_cast(pExt); + IccJson elemObj; + elemObj["type"] = icJsonGetElemTypeName(pElem->GetType()); + elemObj["inputChannels"] = (int)pElem->NumInputChannels(); + elemObj["outputChannels"] = (int)pElem->NumOutputChannels(); + if (pElem->m_nReserved) + elemObj["Reserved"] = (int)pElem->m_nReserved; + pJsonElem->ToJson(elemObj); + elems.push_back(elemObj); + } + } + j["elements"] = elems; + return true; +} + +bool CIccTagJsonMultiProcessElement::ParseJson(const IccJson &j, std::string &parseStr) +{ + int nIn = 0, nOut = 0; + jGetValue(j, "inputChannels", nIn); + jGetValue(j, "outputChannels", nOut); + m_nInputChannels = (icUInt16Number)nIn; + m_nOutputChannels = (icUInt16Number)nOut; + + if (!jsonExistsField(j, "elements") || !j["elements"].is_array()) return true; + + for (const auto &elemObj : j["elements"]) { + if (!elemObj.is_object() || elemObj.empty()) continue; + std::string typeName; + jGetString(elemObj, "type", typeName); + if (typeName.empty()) continue; + + icElemTypeSignature sig = icJsonGetElemTypeNameSig(typeName.c_str()); + CIccMultiProcessElement *pElem = CIccMpeCreator::CreateElement(sig); + if (!pElem) continue; + + int nReserved = 0; + if (jGetValue(elemObj, "Reserved", nReserved)) + pElem->m_nReserved = (icUInt32Number)nReserved; + + IIccExtensionMpe *pExt = pElem->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccMpeJson") != 0) { delete pElem; continue; } + + CIccMpeJson *pJsonElem = static_cast(pExt); + pJsonElem->ParseJson(elemObj, parseStr); + + Attach(pElem); + } + return true; +} + +CIccMultiProcessElement* CIccTagJsonMultiProcessElement::CreateElement(const icChar *szElementName) +{ + icElemTypeSignature sig = icJsonGetElemTypeNameSig(szElementName); + return CIccMpeCreator::CreateElement(sig); +} + +// =========================================================================== +// CIccTagJsonDict +// =========================================================================== + +// Helper: serialize a CIccTagMultiLocalizedUnicode into a JSON array of +// { language, country, text } objects — reuses the same shape as +// CIccTagJsonMultiLocalizedUnicode::ToJson. +static IccJson dictLocalizedToJson(const CIccTagMultiLocalizedUnicode *pTag) +{ + IccJson arr = IccJson::array(); + if (!pTag || !pTag->m_Strings) + return arr; + for (auto i = pTag->m_Strings->begin(); i != pTag->m_Strings->end(); ++i) { + char lang[3] = {0}, country[3] = {0}; + lang[0] = (char)(i->m_nLanguageCode >> 8); + lang[1] = (char)(i->m_nLanguageCode & 0xFF); + country[0] = (char)(i->m_nCountryCode >> 8); + country[1] = (char)(i->m_nCountryCode & 0xFF); + std::string text; + if (i->GetBuf() && i->GetLength() > 0) + icUtf16ToUtf8(text, i->GetBuf(), (int)i->GetLength()); + IccJson loc; + loc["language"] = lang; + loc["country"] = country; + loc["text"] = text; + arr.push_back(loc); + } + return arr; +} + +// Helper: parse a JSON localized array into a new CIccTagMultiLocalizedUnicode. +// Returns nullptr on empty input (caller should not attach the tag). +static CIccTagMultiLocalizedUnicode *dictLocalizedFromJson(const IccJson &arr) +{ + if (!arr.is_array() || arr.empty()) + return nullptr; + CIccTagMultiLocalizedUnicode *pTag = new CIccTagMultiLocalizedUnicode(); + for (const auto &loc : arr) { + std::string lang = loc.contains("language") ? loc["language"].get() : " "; + std::string country = loc.contains("country") ? loc["country"].get() : " "; + std::string text = loc.contains("text") ? loc["text"].get() : ""; + while (lang.size() < 2) lang += ' '; + while (country.size() < 2) country += ' '; + icLanguageCode langCode = (icLanguageCode)((lang[0] << 8) | lang[1]); + icCountryCode countryCode = (icCountryCode) ((country[0] << 8) | country[1]); + CIccUTF16String wstr(text.c_str()); + pTag->SetText(wstr.c_str(), langCode, countryCode); + } + return pTag; +} + +bool CIccTagJsonDict::ToJson(IccJson &j) +{ + IccJson entries = IccJson::array(); + + CIccNameValueDict::iterator nvp; + for (nvp = m_Dict->begin(); nvp != m_Dict->end(); ++nvp) { + CIccDictEntry *nv = nvp->ptr; + if (!nv) + continue; + + IccJson entry; + + // Name (stored as wstring / UTF-16 on Windows) + std::string name; + const std::wstring &wname = nv->GetName(); + icUtf16ToUtf8(name, (const icUInt16Number*)wname.c_str(), (int)wname.size()); + entry["name"] = name; + + // Optional value + if (nv->IsValueSet()) { + std::string value; + const std::wstring wval = nv->GetValue(); + icUtf16ToUtf8(value, (const icUInt16Number*)wval.c_str(), (int)wval.size()); + entry["value"] = value; + } + + // Optional localized name + if (nv->GetNameLocalized()) + entry["localizedNames"] = dictLocalizedToJson(nv->GetNameLocalized()); + + // Optional localized value + if (nv->GetValueLocalized()) + entry["localizedValues"] = dictLocalizedToJson(nv->GetValueLocalized()); + + entries.push_back(entry); + } + + j["entries"] = entries; + return true; +} + +bool CIccTagJsonDict::ParseJson(const IccJson &j, std::string &parseStr) +{ + if (!jsonExistsField(j, "entries") || !j["entries"].is_array()) { + parseStr += "Missing or invalid 'entries' array in Dict tag\n"; + return false; + } + + m_Dict->clear(); + + for (const auto &entry : j["entries"]) { + std::string name; + if (!jGetString(entry, "name", name)) { + parseStr += "Dict entry missing 'name'\n"; + return false; + } + + CIccDictEntry *pDesc = new CIccDictEntry(); + if (!pDesc) + return false; + + // Name: UTF-8 → UTF-16 → wstring + CIccUTF16String wname(name.c_str()); + wname.ToWString(pDesc->GetName()); + + // Optional value + std::string val; + if (jGetString(entry, "value", val)) { + CIccUTF16String wval(val.c_str()); + std::wstring wvalStr; + wval.ToWString(wvalStr); + pDesc->SetValue(wvalStr); + } + + // Optional localized name + if (jsonExistsField(entry, "localizedNames")) { + CIccTagMultiLocalizedUnicode *pTag = dictLocalizedFromJson(entry["localizedNames"]); + if (pTag) + pDesc->SetNameLocalized(pTag); + } + + // Optional localized value + if (jsonExistsField(entry, "localizedValues")) { + CIccTagMultiLocalizedUnicode *pTag = dictLocalizedFromJson(entry["localizedValues"]); + if (pTag) + pDesc->SetValueLocalized(pTag); + } + + CIccDictEntryPtr ptr; + ptr.ptr = pDesc; + m_Dict->push_back(ptr); + } + + return true; +} + +// =========================================================================== +// CIccTagJsonStruct +// =========================================================================== + +bool CIccTagJsonStruct::ToJson(IccJson &j) +{ + const size_t bufSize = 256; + char buf[bufSize]; + + IIccStruct *pStruct = GetStructHandler(); + + // Emit the struct type name so round-trip parsing can restore the struct type + std::string structName; + if (pStruct && strcmp(pStruct->GetDisplayName(), "privateStruct")) { + structName = pStruct->GetDisplayName(); + } else { + structName = "privateStruct"; + j["structureSignature"] = icGetSigStr(buf, bufSize, m_sigStructType); + } + j["structureType"] = structName; + + // Member tags — array of single-key objects: { "": { "type": ..., ...fields... } } + IccJson memberTags = IccJson::array(); + std::set sigSet; + std::map ptrToFirstKey; + int privateElemCount = 0; + + for (auto i = m_ElemEntries->begin(); i != m_ElemEntries->end(); ++i) { + if (sigSet.count(i->TagInfo.sig)) + continue; + sigSet.insert(i->TagInfo.sig); + + CIccTag *pTag = FindElem(i->TagInfo.sig); + if (!pTag) + continue; + + // Determine element name + std::string elemName = pStruct ? pStruct->GetElemName((icSignature)i->TagInfo.sig) : ""; + bool isPrivate = elemName.empty() || !elemName.compare(0, 13, "PrivateSubTag"); + std::string key = isPrivate + ? "PrivateSubTag_" + std::to_string(++privateElemCount) + : elemName; + + IccJson elemObj; + if (isPrivate) + elemObj["sig"] = icGetSigStr(buf, bufSize, i->TagInfo.sig); + + // SameAs: already serialized element + auto prevIt = ptrToFirstKey.find(pTag); + if (prevIt != ptrToFirstKey.end()) { + elemObj["sameAs"] = prevIt->second; + IccJson entry; + entry[key] = elemObj; + memberTags.push_back(entry); + continue; + } + + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) + continue; + + CIccTagJson *pJsonTag = static_cast(pExt); + IccJson tagData; + if (!pJsonTag->ToJson(tagData)) + continue; + + const icChar *typeName = CIccTagCreator::GetTagTypeSigName(pTag->GetType()); + if (!typeName) typeName = "PrivateType"; + elemObj["type"] = typeName; + if (!strcmp(typeName, "PrivateType")) + elemObj["typeSig"] = icGetSigStr(buf, bufSize, pTag->GetType()); + if (pTag->m_nReserved) + elemObj["Reserved"] = (unsigned int)pTag->m_nReserved; + elemObj.update(tagData); + + IccJson entry; + entry[key] = elemObj; + memberTags.push_back(entry); + ptrToFirstKey[pTag] = key; + } + j["memberTags"] = memberTags; + return true; +} + +bool CIccTagJsonStruct::ParseTag(const IccJson &elemEntry, std::string &parseStr) +{ + if (!elemEntry.is_object() || elemEntry.size() != 1) { + parseStr += "MemberTag entry must be a single-member object\n"; + return false; + } + auto it = elemEntry.begin(); + const std::string &key = it.key(); + const IccJson &elemObj = it.value(); + + // Determine element signature + IIccStruct *pStruct = GetStructHandler(); + icSignature sigElem = pStruct ? pStruct->GetElemSig(key.c_str()) : (icSignature)0; + if (!sigElem) { + // Private sub-tag — must carry a "sig" member + std::string sigStr; + jGetString(elemObj, "sig", sigStr); + if (sigStr.empty()) { + parseStr += "PrivateSubTag '" + key + "' missing 'sig'\n"; + return false; + } + sigElem = (icSignature)icGetSigVal(sigStr.c_str()); + } + + // SameAs: re-attach an already-parsed element + if (elemObj.contains("sameAs")) { + std::string refKey = elemObj["sameAs"].get(); + icSignature refSig = pStruct ? pStruct->GetElemSig(refKey.c_str()) : 0; + if (!refSig) { + // refKey may be a "PrivateSubTag_N" — look up by walking existing entries + for (auto ei = m_ElemEntries->begin(); ei != m_ElemEntries->end(); ++ei) { + std::string n = pStruct ? pStruct->GetElemName((icSignature)ei->TagInfo.sig) : ""; + if (n == refKey) { refSig = ei->TagInfo.sig; break; } + } + } + CIccTag *pRefTag = refSig ? FindElem(refSig) : nullptr; + if (!pRefTag) { + parseStr += "SameAs '" + refKey + "' not found for '" + key + "'\n"; + return false; + } + if (!AttachElem(sigElem, pRefTag)) { + parseStr += "Unable to attach SameAs element '" + key + "'\n"; + return false; + } + return true; + } + + // Flat object with "type" discriminator + std::string typeName; + jGetString(elemObj, "type", typeName); + if (typeName.empty()) { + parseStr += "MemberTag '" + key + "' missing 'type' field\n"; + return false; + } + + icTagTypeSignature typeSig = CIccTagCreator::GetTagTypeNameSig(typeName.c_str()); + if (typeSig == icSigUnknownType) { + if (typeName == "PrivateType" && elemObj.contains("typeSig")) + typeSig = (icTagTypeSignature)icGetSigVal(elemObj["typeSig"].get().c_str()); + else + typeSig = (icTagTypeSignature)icGetSigVal(typeName.c_str()); + } + + CIccTag *pTag = CIccTagCreator::CreateTag(typeSig); + if (!pTag) { + parseStr += "Unable to create type '" + typeName + "' for member '" + key + "'\n"; + return false; + } + + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) { + delete pTag; + parseStr += "Type '" + typeName + "' does not support JSON\n"; + return false; + } + + CIccTagJson *pJsonTag = static_cast(pExt); + if (!pJsonTag->ParseJson(elemObj, parseStr)) { + delete pTag; + return false; + } + + if (elemObj.contains("Reserved")) + pTag->m_nReserved = (icUInt32Number)elemObj["Reserved"].get(); + + if (!AttachElem(sigElem, pTag)) { + delete pTag; + parseStr += "Unable to attach member '" + key + "'\n"; + return false; + } + return true; +} + +bool CIccTagJsonStruct::ParseJson(const IccJson &j, std::string &parseStr) +{ + // Resolve struct type + std::string structTypeName; + jGetString(j, "structureType", structTypeName); + + icStructSignature sigStruct = (icStructSignature)0; + if (!structTypeName.empty() && structTypeName != "privateStruct") { + sigStruct = CIccStructCreator::GetStructSig(structTypeName.c_str()); + } + if (!sigStruct) { + // Fall back to explicit signature field + std::string sigStr; + jGetString(j, "structureSignature", sigStr); + if (!sigStr.empty()) + sigStruct = (icStructSignature)icGetSigVal(sigStr.c_str()); + } + if (sigStruct) + SetTagStructType(sigStruct); + + // Parse memberTags array + if (!j.contains("memberTags") || !j["memberTags"].is_array()) { + parseStr += "Struct tag missing 'memberTags' array\n"; + return false; + } + for (const auto &entry : j["memberTags"]) { + if (!ParseTag(entry, parseStr)) + parseStr += "Warning: skipped member tag\n"; + } + return true; +} + +// =========================================================================== +// CIccTagJsonArray +// =========================================================================== + +bool CIccTagJsonArray::ToJson(IccJson &j) +{ + const size_t bufSize = 256; + char buf[bufSize]; + + // Emit array type so round-trip can restore it + std::string arrayName; + if (CIccArrayCreator::GetArraySigName(arrayName, m_sigArrayType, false)) { + j["arrayType"] = arrayName; + } else { + j["arrayType"] = "privateArray"; + j["arraySignature"] = icGetSigStr(buf, bufSize, m_sigArrayType); + } + + // Elements — flat objects with "type" discriminator field + IccJson elems = IccJson::array(); + for (icUInt32Number i = 0; i < m_nSize; i++) { + CIccTag *pTag = m_TagVals[i].ptr; + IccJson elemObj; + if (!pTag) { + elemObj["type"] = nullptr; + elems.push_back(elemObj); + continue; + } + + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) { + elemObj["type"] = nullptr; + elems.push_back(elemObj); + continue; + } + + CIccTagJson *pJsonTag = static_cast(pExt); + IccJson tagData; + if (!pJsonTag->ToJson(tagData)) { + elemObj["type"] = nullptr; + elems.push_back(elemObj); + continue; + } + + const icChar *typeName = CIccTagCreator::GetTagTypeSigName(pTag->GetType()); + if (!typeName) typeName = "PrivateType"; + elemObj["type"] = typeName; + if (!strcmp(typeName, "PrivateType")) + elemObj["sig"] = icGetSigStr(buf, bufSize, pTag->GetType()); + if (pTag->m_nReserved) + elemObj["Reserved"] = (unsigned int)pTag->m_nReserved; + elemObj.update(tagData); + + elems.push_back(elemObj); + } + j["arrayTags"] = elems; + return true; +} + +bool CIccTagJsonArray::ParseJson(const IccJson &j, std::string &parseStr) +{ + // Resolve array type + std::string arrayTypeName; + jGetString(j, "arrayType", arrayTypeName); + + icArraySignature sigArray = (icArraySignature)0; + if (!arrayTypeName.empty() && arrayTypeName != "privateArray") { + sigArray = CIccArrayCreator::GetArraySig(arrayTypeName.c_str()); + } + if (!sigArray) { + std::string sigStr; + jGetString(j, "arraySignature", sigStr); + if (!sigStr.empty()) + sigArray = (icArraySignature)icGetSigVal(sigStr.c_str()); + } + if (sigArray) + SetTagArrayType(sigArray); + + // Parse arrayTags + if (!j.contains("arrayTags") || !j["arrayTags"].is_array()) { + parseStr += "Array tag missing 'arrayTags' array\n"; + return false; + } + const IccJson &elems = j["arrayTags"]; + icUInt32Number nSize = (icUInt32Number)elems.size(); + if (!SetSize(nSize)) { + parseStr += "Unable to allocate array of size " + std::to_string(nSize) + "\n"; + return false; + } + + for (icUInt32Number i = 0; i < nSize; i++) { + const IccJson &elemObj = elems[i]; + + // Flat object with "type" discriminator + std::string typeName; + jGetString(elemObj, "type", typeName); + if (typeName.empty()) { + parseStr += "Warning: array element " + std::to_string(i) + " has no type, skipping\n"; + continue; + } + + icTagTypeSignature typeSig = CIccTagCreator::GetTagTypeNameSig(typeName.c_str()); + if (typeSig == icSigUnknownType) { + if (typeName == "PrivateType" && elemObj.contains("sig")) + typeSig = (icTagTypeSignature)icGetSigVal(elemObj["sig"].get().c_str()); + else + typeSig = (icTagTypeSignature)icGetSigVal(typeName.c_str()); + } + + CIccTag *pTag = CIccTagCreator::CreateTag(typeSig); + if (!pTag) { + parseStr += "Unable to create type '" + typeName + "' for array element " + std::to_string(i) + "\n"; + return false; + } + + IIccExtensionTag *pExt = pTag->GetExtension(); + if (!pExt || strcmp(pExt->GetExtClassName(), "CIccTagJson") != 0) { + delete pTag; + parseStr += "Type '" + typeName + "' does not support JSON\n"; + return false; + } + + CIccTagJson *pJsonTag = static_cast(pExt); + if (!pJsonTag->ParseJson(elemObj, parseStr)) { + delete pTag; + return false; + } + + if (elemObj.contains("Reserved")) + pTag->m_nReserved = (icUInt32Number)elemObj["Reserved"].get(); + + if (!AttachTag(i, pTag)) { + delete pTag; + parseStr += "Unable to attach array element " + std::to_string(i) + "\n"; + return false; + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonGamutBoundaryDesc +// =========================================================================== + +bool CIccTagJsonGamutBoundaryDesc::ToJson(IccJson &j) +{ + if (m_NumberOfVertices > 0 && (m_PCSValues || m_DeviceValues)) { + IccJson vertices; + if (m_PCSValues) { + vertices["PCSChannels"] = (int)m_nPCSChannels; + IccJson pcsArr = IccJson::array(); + icInt32Number nPCS = m_NumberOfVertices * m_nPCSChannels; + for (icInt32Number i = 0; i < nPCS; i++) + pcsArr.push_back((double)m_PCSValues[i]); + vertices["PCSValues"] = pcsArr; + } + if (m_DeviceValues) { + vertices["DeviceChannels"] = (int)m_nDeviceChannels; + IccJson devArr = IccJson::array(); + icInt32Number nDev = m_NumberOfVertices * m_nDeviceChannels; + for (icInt32Number i = 0; i < nDev; i++) + devArr.push_back((double)m_DeviceValues[i]); + vertices["DeviceValues"] = devArr; + } + j["Vertices"] = vertices; + } + + if (m_Triangles && m_NumberOfTriangles > 0) { + IccJson triArr = IccJson::array(); + for (icInt32Number i = 0; i < m_NumberOfTriangles; i++) { + IccJson tri = IccJson::array(); + tri.push_back((icUInt32Number)m_Triangles[i].m_VertexNumbers[0]); + tri.push_back((icUInt32Number)m_Triangles[i].m_VertexNumbers[1]); + tri.push_back((icUInt32Number)m_Triangles[i].m_VertexNumbers[2]); + triArr.push_back(tri); + } + j["Triangles"] = triArr; + } + + return true; +} + +bool CIccTagJsonGamutBoundaryDesc::ParseJson(const IccJson &j, std::string &parseStr) +{ + if (!j.contains("Vertices") || !j["Vertices"].is_object()) { + parseStr += "Cannot find Vertices\n"; + return false; + } + const IccJson &verts = j["Vertices"]; + + // Parse PCS values + if (!verts.contains("PCSChannels") || !verts.contains("PCSValues") || !verts["PCSValues"].is_array()) { + parseStr += "Cannot find PCSValues\n"; + return false; + } + m_nPCSChannels = (icInt16Number)verts["PCSChannels"].get(); + if (m_nPCSChannels <= 0) { + parseStr += "Bad PCSValues channels\n"; + return false; + } + const IccJson &pcsArr = verts["PCSValues"]; + icInt32Number nPCSTotal = (icInt32Number)pcsArr.size(); + m_NumberOfVertices = nPCSTotal / m_nPCSChannels; + if (m_NumberOfVertices < 4) { + parseStr += "Must have at least 4 PCSValues vertices\n"; + return false; + } + if (m_PCSValues) { delete[] m_PCSValues; } + m_PCSValues = new icFloatNumber[m_NumberOfVertices * m_nPCSChannels]; + for (icInt32Number i = 0; i < nPCSTotal; i++) + m_PCSValues[i] = (icFloatNumber)pcsArr[i].get(); + + // Parse optional Device values + if (verts.contains("DeviceChannels") && verts.contains("DeviceValues") && verts["DeviceValues"].is_array()) { + m_nDeviceChannels = (icInt16Number)verts["DeviceChannels"].get(); + if (m_nDeviceChannels <= 0) { + parseStr += "Bad DeviceValues channels\n"; + return false; + } + const IccJson &devArr = verts["DeviceValues"]; + icInt32Number nDevTotal = (icInt32Number)devArr.size(); + icInt32Number nDevVerts = nDevTotal / m_nDeviceChannels; + if (nDevVerts != m_NumberOfVertices) { + parseStr += "Number of Device vertices doesn't match PCS vertices\n"; + return false; + } + if (m_DeviceValues) { delete[] m_DeviceValues; } + m_DeviceValues = new icFloatNumber[m_NumberOfVertices * m_nDeviceChannels]; + for (icInt32Number i = 0; i < nDevTotal; i++) + m_DeviceValues[i] = (icFloatNumber)devArr[i].get(); + } + + // Parse triangles + if (!j.contains("Triangles") || !j["Triangles"].is_array()) { + parseStr += "Cannot find Triangles\n"; + return false; + } + const IccJson &triArr = j["Triangles"]; + m_NumberOfTriangles = (icInt32Number)triArr.size(); + if (m_Triangles) { delete[] m_Triangles; } + m_Triangles = new icGamutBoundaryTriangle[m_NumberOfTriangles]; + for (icInt32Number i = 0; i < m_NumberOfTriangles; i++) { + const IccJson &tri = triArr[i]; + if (!tri.is_array() || tri.size() != 3) { + parseStr += "Invalid Triangle entry\n"; + return false; + } + m_Triangles[i].m_VertexNumbers[0] = tri[0].get(); + m_Triangles[i].m_VertexNumbers[1] = tri[1].get(); + m_Triangles[i].m_VertexNumbers[2] = tri[2].get(); + } + + return true; +} + +// =========================================================================== +// CIccTagJsonEmbeddedHeightImage +// =========================================================================== + +bool CIccTagJsonEmbeddedHeightImage::ToJson(IccJson &j) +{ + j["SeamlessIndicator"] = (unsigned int)m_nSeamlesIndicator; + j["EncodingFormat"] = (unsigned int)m_nEncodingFormat; + j["MetersMinPixelValue"] = (double)m_fMetersMinPixelValue; + j["MetersMaxPixelValue"] = (double)m_fMetersMaxPixelValue; + if (m_pData && m_nSize) + j["ImageData"] = icJsonDumpHexData(m_pData, m_nSize); + return true; +} + +bool CIccTagJsonEmbeddedHeightImage::ParseJson(const IccJson &j, std::string &parseStr) +{ + m_nSeamlesIndicator = 0; + m_nEncodingFormat = icPngImageType; + m_fMetersMinPixelValue = 0.0f; + m_fMetersMaxPixelValue = 0.0f; + + jGetValue(j, "SeamlessIndicator", m_nSeamlesIndicator); + unsigned int nEncFmt = (unsigned int)icPngImageType; + jGetValue(j, "EncodingFormat", nEncFmt); + m_nEncodingFormat = (icImageEncodingType)nEncFmt; + jGetValue(j, "MetersMinPixelValue", m_fMetersMinPixelValue); + jGetValue(j, "MetersMaxPixelValue", m_fMetersMaxPixelValue); + + std::string hex; + if (!jGetString(j, "ImageData", hex)) { + parseStr += "Cannot find ImageData in HeightImage\n"; + return false; + } + icUInt32Number nSize = icJsonGetHexDataSize(hex.c_str()); + if (nSize) { + SetSize(nSize); + if (icJsonGetHexData(m_pData, hex.c_str(), m_nSize) != m_nSize) { + parseStr += "Failed to decode HeightImage ImageData\n"; + return false; + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonEmbeddedNormalImage +// =========================================================================== + +bool CIccTagJsonEmbeddedNormalImage::ToJson(IccJson &j) +{ + j["SeamlessIndicator"] = (unsigned int)m_nSeamlesIndicator; + j["EncodingFormat"] = (unsigned int)m_nEncodingFormat; + if (m_pData && m_nSize) + j["ImageData"] = icJsonDumpHexData(m_pData, m_nSize); + return true; +} + +bool CIccTagJsonEmbeddedNormalImage::ParseJson(const IccJson &j, std::string &parseStr) +{ + m_nSeamlesIndicator = 0; + m_nEncodingFormat = icPngImageType; + + jGetValue(j, "SeamlessIndicator", m_nSeamlesIndicator); + unsigned int nEncFmt = (unsigned int)icPngImageType; + jGetValue(j, "EncodingFormat", nEncFmt); + m_nEncodingFormat = (icImageEncodingType)nEncFmt; + + std::string hex; + if (!jGetString(j, "ImageData", hex)) { + parseStr += "Cannot find ImageData in NormalImage\n"; + return false; + } + icUInt32Number nSize = icJsonGetHexDataSize(hex.c_str()); + if (nSize) { + SetSize(nSize); + if (icJsonGetHexData(m_pData, hex.c_str(), m_nSize) != m_nSize) { + parseStr += "Failed to decode NormalImage ImageData\n"; + return false; + } + } + return true; +} + +// =========================================================================== +// CIccTagJsonEmbeddedProfile +// =========================================================================== + +bool CIccTagJsonEmbeddedProfile::ToJson(IccJson &j) +{ + if (!m_pProfile) + return false; + + if (strcmp(m_pProfile->GetClassName(), "CIccProfileJson") != 0) + return false; + + CIccProfileJson *pProfile = static_cast(m_pProfile); + return pProfile->ToJson(j["IccProfile"]); +} + +bool CIccTagJsonEmbeddedProfile::ParseJson(const IccJson &j, std::string &parseStr) +{ + if (!j.contains("IccProfile") || !j["IccProfile"].is_object()) { + parseStr += "Cannot find IccProfile in embedded profile tag\n"; + return false; + } + + CIccProfileJson *pProfile = new CIccProfileJson(); + if (!pProfile->ParseJson(j["IccProfile"], parseStr)) { + delete pProfile; + return false; + } + + SetProfile(pProfile); + return true; +} + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif diff --git a/IccJSON/IccLibJSON/IccTagJson.h b/IccJSON/IccLibJSON/IccTagJson.h new file mode 100644 index 000000000..5c1ffdba6 --- /dev/null +++ b/IccJSON/IccLibJSON/IccTagJson.h @@ -0,0 +1,675 @@ +/** @file +File: IccTagJson.h + +Contains: Header for implementation of CIccTagJson class and + creation factories + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCTAGJSON_H +#define _ICCTAGJSON_H + +#include "IccTag.h" +#include "IccTagMPE.h" +#include "IccJsonConfig.h" +#include + +using IccJson = nlohmann::json; + +// --------------------------------------------------------------------------- +// Base extension interface +// --------------------------------------------------------------------------- +class CIccTagJson : public IIccExtensionTag +{ +public: + virtual ~CIccTagJson() {} + + virtual const char *GetExtClassName() const { return "CIccTagJson"; } + virtual const char *GetExtDerivedClassName() const { return ""; } + + virtual bool ToJson(IccJson &j) = 0; + virtual bool ParseJson(const IccJson &j, std::string &parseStr) = 0; +}; + +// --------------------------------------------------------------------------- +// Unknown tag +// --------------------------------------------------------------------------- +class CIccTagJsonUnknown : public CIccTagUnknown, public CIccTagJson +{ +public: + CIccTagJsonUnknown(icTagTypeSignature nType) { m_nType = nType; } + virtual ~CIccTagJsonUnknown() {} + + virtual const char *GetClassName() const { return "CIccTagJsonUnknown"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Text tags +// --------------------------------------------------------------------------- +class CIccTagJsonText : public CIccTagText, public CIccTagJson +{ +public: + CIccTagJsonText() : CIccTagText() {} + CIccTagJsonText(const CIccTagJsonText &t) : CIccTagText(t) {} + virtual ~CIccTagJsonText() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonText(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonText"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonUtf8Text : public CIccTagUtf8Text, public CIccTagJson +{ +public: + CIccTagJsonUtf8Text() : CIccTagUtf8Text() {} + CIccTagJsonUtf8Text(const CIccTagJsonUtf8Text &t) : CIccTagUtf8Text(t) {} + virtual ~CIccTagJsonUtf8Text() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonUtf8Text(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonUtf8Text"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonZipUtf8Text : public CIccTagZipUtf8Text, public CIccTagJson +{ +public: + CIccTagJsonZipUtf8Text() : CIccTagZipUtf8Text() {} + CIccTagJsonZipUtf8Text(const CIccTagJsonZipUtf8Text &t) : CIccTagZipUtf8Text(t) {} + virtual ~CIccTagJsonZipUtf8Text() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonZipUtf8Text(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonZipUtf8Text"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonZipXml : public CIccTagZipXml, public CIccTagJson +{ +public: + CIccTagJsonZipXml() : CIccTagZipXml() {} + CIccTagJsonZipXml(const CIccTagJsonZipXml &t) : CIccTagZipXml(t) {} + virtual ~CIccTagJsonZipXml() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonZipXml(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonZipXml"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonUtf16Text : public CIccTagUtf16Text, public CIccTagJson +{ +public: + CIccTagJsonUtf16Text() : CIccTagUtf16Text() {} + CIccTagJsonUtf16Text(const CIccTagJsonUtf16Text &t) : CIccTagUtf16Text(t) {} + virtual ~CIccTagJsonUtf16Text() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonUtf16Text(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonUtf16Text"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonTextDescription : public CIccTagTextDescription, public CIccTagJson +{ +public: + CIccTagJsonTextDescription() : CIccTagTextDescription() {} + CIccTagJsonTextDescription(const CIccTagJsonTextDescription &t) : CIccTagTextDescription(t) {} + virtual ~CIccTagJsonTextDescription() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonTextDescription(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonTextDescription"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Signature / basic value tags +// --------------------------------------------------------------------------- +class CIccTagJsonSignature : public CIccTagSignature, public CIccTagJson +{ +public: + virtual ~CIccTagJsonSignature() {} + virtual const char *GetClassName() const { return "CIccTagJsonSignature"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonDateTime : public CIccTagDateTime, public CIccTagJson +{ +public: + virtual ~CIccTagJsonDateTime() {} + virtual const char *GetClassName() const { return "CIccTagJsonDateTime"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Colorimetric / measurement tags +// --------------------------------------------------------------------------- +class CIccTagJsonXYZ : public CIccTagXYZ, public CIccTagJson +{ +public: + virtual ~CIccTagJsonXYZ() {} + virtual const char *GetClassName() const { return "CIccTagJsonXYZ"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonChromaticity : public CIccTagChromaticity, public CIccTagJson +{ +public: + virtual ~CIccTagJsonChromaticity() {} + virtual const char *GetClassName() const { return "CIccTagJsonChromaticity"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonCicp : public CIccTagCicp, public CIccTagJson +{ +public: + virtual ~CIccTagJsonCicp() {} + virtual const char *GetClassName() const { return "CIccTagJsonCicp"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonMeasurement : public CIccTagMeasurement, public CIccTagJson +{ +public: + virtual ~CIccTagJsonMeasurement() {} + virtual const char *GetClassName() const { return "CIccTagJsonMeasurement"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonViewingConditions : public CIccTagViewingConditions, public CIccTagJson +{ +public: + virtual ~CIccTagJsonViewingConditions() {} + virtual const char *GetClassName() const { return "CIccTagJsonViewingConditions"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonSpectralDataInfo : public CIccTagSpectralDataInfo, public CIccTagJson +{ +public: + virtual ~CIccTagJsonSpectralDataInfo() {} + virtual const char *GetClassName() const { return "CIccTagJsonSpectralDataInfo"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonSpectralViewingConditions : public CIccTagSpectralViewingConditions, public CIccTagJson +{ +public: + virtual ~CIccTagJsonSpectralViewingConditions() {} + virtual const char *GetClassName() const { return "CIccTagJsonSpectralViewingConditions"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Named / colorant tags +// --------------------------------------------------------------------------- +class CIccTagJsonNamedColor2 : public CIccTagNamedColor2, public CIccTagJson +{ +public: + virtual ~CIccTagJsonNamedColor2() {} + virtual const char *GetClassName() const { return "CIccTagJsonNamedColor2"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonColorantOrder : public CIccTagColorantOrder, public CIccTagJson +{ +public: + virtual ~CIccTagJsonColorantOrder() {} + virtual const char *GetClassName() const { return "CIccTagJsonColorantOrder"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonColorantTable : public CIccTagColorantTable, public CIccTagJson +{ +public: + virtual ~CIccTagJsonColorantTable() {} + virtual const char *GetClassName() const { return "CIccTagJsonColorantTable"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Numeric array tags +// --------------------------------------------------------------------------- +class CIccTagJsonSparseMatrixArray : public CIccTagSparseMatrixArray, public CIccTagJson +{ +public: + virtual ~CIccTagJsonSparseMatrixArray() {} + virtual const char *GetClassName() const { return "CIccTagJsonSparseMatrixArray"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +template +class CIccTagJsonFixedNum : public CIccTagFixedNum, public CIccTagJson +{ +public: + virtual ~CIccTagJsonFixedNum() {} + virtual const char *GetClassName() const; + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +typedef CIccTagJsonFixedNum CIccTagJsonS15Fixed16; +typedef CIccTagFixedNum CIccTagJsonU16Fixed16; + +// Forward declaration for array helper used by CIccTagJsonNum +template class CIccJsonArrayType; + +template +class CIccTagJsonNum : public CIccTagNum, public CIccTagJson +{ +public: + virtual ~CIccTagJsonNum() {} + virtual const char *GetClassName() const; + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +typedef CIccTagJsonNum, icSigUInt8ArrayType> CIccTagJsonUInt8; +typedef CIccTagJsonNum, icSigUInt16ArrayType> CIccTagJsonUInt16; +typedef CIccTagJsonNum, icSigUInt32ArrayType> CIccTagJsonUInt32; +typedef CIccTagJsonNum, icSigUInt64ArrayType> CIccTagJsonUInt64; + +template +class CIccTagJsonFloatNum : public CIccTagFloatNum, public CIccTagJson +{ +public: + virtual ~CIccTagJsonFloatNum() {} + virtual const char *GetClassName() const; + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +typedef CIccTagJsonFloatNum, icSigFloat16ArrayType> CIccTagJsonFloat16; +typedef CIccTagJsonFloatNum, icSigFloat32ArrayType> CIccTagJsonFloat32; +typedef CIccTagJsonFloatNum, icSigFloat64ArrayType> CIccTagJsonFloat64; + +// --------------------------------------------------------------------------- +// Sequence / description tags +// --------------------------------------------------------------------------- +class CIccTagJsonMultiLocalizedUnicode : public CIccTagMultiLocalizedUnicode, public CIccTagJson +{ +public: + CIccTagJsonMultiLocalizedUnicode() : CIccTagMultiLocalizedUnicode() {} + CIccTagJsonMultiLocalizedUnicode(const CIccTagJsonMultiLocalizedUnicode &t) : CIccTagMultiLocalizedUnicode(t) {} + virtual ~CIccTagJsonMultiLocalizedUnicode() {} + + virtual CIccTag* NewCopy() const { return new CIccTagJsonMultiLocalizedUnicode(*this); } + virtual const char *GetClassName() const { return "CIccTagJsonMultiLocalizedUnicode"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonProfileSeqDesc : public CIccTagProfileSeqDesc, public CIccTagJson +{ +public: + virtual ~CIccTagJsonProfileSeqDesc() {} + virtual const char *GetClassName() const { return "CIccTagJsonProfileSeqDesc"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonProfileSequenceId : public CIccTagProfileSequenceId, public CIccTagJson +{ +public: + virtual ~CIccTagJsonProfileSequenceId() {} + virtual const char *GetClassName() const { return "CIccTagJsonProfileSequenceId"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Data tag +// --------------------------------------------------------------------------- +class CIccTagJsonTagData : public CIccTagData, public CIccTagJson +{ +public: + virtual ~CIccTagJsonTagData() {} + virtual const char *GetClassName() const { return "CIccTagJsonTagData"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Response curve +// --------------------------------------------------------------------------- +class CIccTagJsonResponseCurveSet16 : public CIccTagResponseCurveSet16, public CIccTagJson +{ +public: + virtual ~CIccTagJsonResponseCurveSet16() {} + virtual const char *GetClassName() const { return "CIccTagJsonResponseCurveSet16"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Curve tags – base interface with convert-type overloads (mirrors CIccCurveXml) +// --------------------------------------------------------------------------- +class CIccCurveJson : public CIccTagJson +{ +public: + virtual ~CIccCurveJson() {} + virtual const char *GetExtDerivedClassName() const { return "CIccCurveJson"; } + + virtual bool ToJson(IccJson &j) = 0; + virtual bool ParseJson(const IccJson &j, std::string &parseStr) = 0; + virtual bool ToJson(IccJson &j, icConvertType nType) = 0; + virtual bool ParseJson(const IccJson &j, icConvertType nType, std::string &parseStr) = 0; +}; + +class CIccTagJsonCurve : public CIccTagCurve, public CIccCurveJson +{ +public: + virtual ~CIccTagJsonCurve() {} + virtual const char *GetClassName() const { return "CIccTagJsonCurve"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ToJson(IccJson &j, icConvertType nType); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); + virtual bool ParseJson(const IccJson &j, icConvertType nType, std::string &parseStr); +}; + +class CIccTagJsonParametricCurve : public CIccTagParametricCurve, public CIccCurveJson +{ +public: + virtual ~CIccTagJsonParametricCurve() {} + virtual const char *GetClassName() const { return "CIccTagJsonParametricCurve"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ToJson(IccJson &j, icConvertType nType); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); + virtual bool ParseJson(const IccJson &j, icConvertType nType, std::string &parseStr); +}; + +class CIccTagJsonSegmentedCurve : public CIccTagSegmentedCurve, public CIccCurveJson +{ +public: + virtual ~CIccTagJsonSegmentedCurve() {} + virtual const char *GetClassName() const { return "CIccTagJsonSegmentedCurve"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ToJson(IccJson &j, icConvertType nType); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); + virtual bool ParseJson(const IccJson &j, icConvertType nType, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// LUT tags +// --------------------------------------------------------------------------- +class CIccTagJsonLutAtoB : public CIccTagLutAtoB, public CIccTagJson +{ +public: + virtual ~CIccTagJsonLutAtoB() {} + virtual const char *GetClassName() const { return "CIccTagJsonLutAtoB"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonLutBtoA : public CIccTagLutBtoA, public CIccTagJson +{ +public: + virtual ~CIccTagJsonLutBtoA() {} + virtual const char *GetClassName() const { return "CIccTagJsonLutBtoA"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonLut8 : public CIccTagLut8, public CIccTagJson +{ +public: + virtual ~CIccTagJsonLut8() {} + virtual const char *GetClassName() const { return "CIccTagJsonLut8"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonLut16 : public CIccTagLut16, public CIccTagJson +{ +public: + virtual ~CIccTagJsonLut16() {} + virtual const char *GetClassName() const { return "CIccTagJsonLut16"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Multi-process element tag +// --------------------------------------------------------------------------- +class CIccTagJsonMultiProcessElement : public CIccTagMultiProcessElement, public CIccTagJson +{ +public: + virtual ~CIccTagJsonMultiProcessElement() {} + virtual const char *GetClassName() const { return "CIccTagJsonMultiProcessElement"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); + + static CIccMultiProcessElement *CreateElement(const icChar *szElementName); + +protected: + bool ParseElement(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Dict / Struct / Array tags +// --------------------------------------------------------------------------- +class CIccTagJsonDict : public CIccTagDict, public CIccTagJson +{ +public: + virtual ~CIccTagJsonDict() {} + virtual const char *GetClassName() const { return "CIccTagJsonDict"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonStruct : public CIccTagStruct, public CIccTagJson +{ +public: + virtual ~CIccTagJsonStruct() {} + virtual const char *GetClassName() const { return "CIccTagJsonStruct"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); + +protected: + bool ParseTag(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonArray : public CIccTagArray, public CIccTagJson +{ +public: + virtual ~CIccTagJsonArray() {} + virtual const char *GetClassName() const { return "CIccTagJsonArray"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +// --------------------------------------------------------------------------- +// Gamut / embedded image / embedded profile tags +// --------------------------------------------------------------------------- +class CIccTagJsonGamutBoundaryDesc : public CIccTagGamutBoundaryDesc, public CIccTagJson +{ +public: + virtual ~CIccTagJsonGamutBoundaryDesc() {} + virtual const char *GetClassName() const { return "CIccTagJsonGamutBoundaryDesc"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonEmbeddedHeightImage : public CIccTagEmbeddedHeightImage, public CIccTagJson +{ +public: + virtual ~CIccTagJsonEmbeddedHeightImage() {} + virtual const char *GetClassName() const { return "CIccTagJsonEmbeddedHeightImage"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonEmbeddedNormalImage : public CIccTagEmbeddedNormalImage, public CIccTagJson +{ +public: + virtual ~CIccTagJsonEmbeddedNormalImage() {} + virtual const char *GetClassName() const { return "CIccTagJsonEmbeddedNormalImage"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +class CIccTagJsonEmbeddedProfile : public CIccTagEmbeddedProfile, public CIccTagJson +{ +public: + virtual ~CIccTagJsonEmbeddedProfile() {} + virtual const char *GetClassName() const { return "CIccTagJsonEmbeddedProfile"; } + virtual IIccExtensionTag *GetExtension() { return this; } + + virtual bool ToJson(IccJson &j); + virtual bool ParseJson(const IccJson &j, std::string &parseStr); +}; + +#endif // _ICCTAGJSON_H diff --git a/IccJSON/IccLibJSON/IccTagJsonFactory.cpp b/IccJSON/IccLibJSON/IccTagJsonFactory.cpp new file mode 100644 index 000000000..031a9eb43 --- /dev/null +++ b/IccJSON/IccLibJSON/IccTagJsonFactory.cpp @@ -0,0 +1,151 @@ +/** @file +File: IccTagJsonFactory.cpp + +Contains: Implementation of the CIccTagJsonFactory class – + creates JSON-aware tag objects for all ICC-specified types. + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccTagJson.h" +#include "IccTagJsonFactory.h" +#include "IccUtil.h" +#include "IccProfile.h" + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +CIccTag* CIccTagJsonFactory::CreateTag(icTagTypeSignature tagSig) +{ + switch (tagSig) { + case icSigSignatureType: return new CIccTagJsonSignature; + case icSigTextType: return new CIccTagJsonText; + case icSigXYZArrayType: return new CIccTagJsonXYZ; + case icSigCicpType: return new CIccTagJsonCicp; + case icSigUInt8ArrayType: return new CIccTagJsonUInt8; + case icSigUInt16ArrayType: return new CIccTagJsonUInt16; + case icSigUInt32ArrayType: return new CIccTagJsonUInt32; + case icSigUInt64ArrayType: return new CIccTagJsonUInt64; + case icSigS15Fixed16ArrayType: return new CIccTagJsonS15Fixed16; + case icSigFloat16ArrayType: return new CIccTagJsonFloat16; + case icSigFloat32ArrayType: return new CIccTagJsonFloat32; + case icSigFloat64ArrayType: return new CIccTagJsonFloat64; + case icSigGamutBoundaryDescType: return new CIccTagJsonGamutBoundaryDesc; + case icSigCurveType: return new CIccTagJsonCurve; + case icSigSegmentedCurveType: return new CIccTagJsonSegmentedCurve; + case icSigMeasurementType: return new CIccTagJsonMeasurement; + case icSigMultiLocalizedUnicodeType: return new CIccTagJsonMultiLocalizedUnicode; + case icSigMultiProcessElementType: return new CIccTagJsonMultiProcessElement; + case icSigParametricCurveType: return new CIccTagJsonParametricCurve; + case icSigLutAtoBType: return new CIccTagJsonLutAtoB; + case icSigLutBtoAType: return new CIccTagJsonLutBtoA; + case icSigLut16Type: return new CIccTagJsonLut16; + case icSigLut8Type: return new CIccTagJsonLut8; + case icSigTextDescriptionType: return new CIccTagJsonTextDescription; + case icSigNamedColor2Type: return new CIccTagJsonNamedColor2; + case icSigChromaticityType: return new CIccTagJsonChromaticity; + case icSigDataType: return new CIccTagJsonTagData; + case icSigDateTimeType: return new CIccTagJsonDateTime; + case icSigColorantOrderType: return new CIccTagJsonColorantOrder; + case icSigColorantTableType: return new CIccTagJsonColorantTable; + case icSigSparseMatrixArrayType: return new CIccTagJsonSparseMatrixArray; + case icSigViewingConditionsType: return new CIccTagJsonViewingConditions; + case icSigSpectralViewingConditionsType: return new CIccTagJsonSpectralViewingConditions; + case icSigSpectralDataInfoType: return new CIccTagJsonSpectralDataInfo; + case icSigProfileSequenceDescType: return new CIccTagJsonProfileSeqDesc; + case icSigResponseCurveSet16Type: return new CIccTagJsonResponseCurveSet16; + case icSigProfileSequceIdType: return new CIccTagJsonProfileSequenceId; + case icSigDictType: return new CIccTagJsonDict; + case icSigTagStructType: return new CIccTagJsonStruct; + case icSigTagArrayType: return new CIccTagJsonArray; + case icSigUtf8TextType: return new CIccTagJsonUtf8Text; + case icSigZipUtf8TextType: return new CIccTagJsonZipUtf8Text; + case icSigZipXmlType: return new CIccTagJsonZipXml; + case icSigUtf16TextType: return new CIccTagJsonUtf16Text; + case icSigEmbeddedProfileType: return new CIccTagJsonEmbeddedProfile; + case icSigEmbeddedHeightImageType: return new CIccTagJsonEmbeddedHeightImage; + case icSigEmbeddedNormalImageType: return new CIccTagJsonEmbeddedNormalImage; + + case icSigScreeningType: + case icSigUcrBgType: + case icSigCrdInfoType: + default: + return new CIccTagJsonUnknown(tagSig); + } +} + +const icChar* CIccTagJsonFactory::GetTagSigName(icTagSignature /*tagSig*/) +{ + return NULL; +} + +icTagSignature CIccTagJsonFactory::GetTagNameSig(const icChar * /*szName*/) +{ + return icSigUnknownTag; +} + +const icChar* CIccTagJsonFactory::GetTagTypeSigName(icTagTypeSignature /*tagTypeSig*/) +{ + return NULL; +} + +icTagTypeSignature CIccTagJsonFactory::GetTagTypeNameSig(const icChar * /*szName*/) +{ + return icSigUnknownType; +} + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif diff --git a/IccJSON/IccLibJSON/IccTagJsonFactory.h b/IccJSON/IccLibJSON/IccTagJsonFactory.h new file mode 100644 index 000000000..79b811552 --- /dev/null +++ b/IccJSON/IccLibJSON/IccTagJsonFactory.h @@ -0,0 +1,95 @@ +/** @file +File: IccTagJsonFactory.h + +Contains: Header for implementation of a CIccTagJsonFactory class – + an extension factory providing ICC JSON format capabilities. + +Version: V1 + +Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCTAGJSONFACTORY_H +#define _ICCTAGJSONFACTORY_H + +#include "IccTagFactory.h" + +#ifdef USEICCDEVNAMESPACE +namespace iccDEV { +#endif + +/** + *********************************************************************** + * Class: CIccTagJsonFactory + * + * Purpose: + * Creates JSON-aware CIccTag objects for all ICC-specified tag types. + * Push an instance onto CIccTagCreator to enable JSON serialization. + *********************************************************************** + */ +class CIccTagJsonFactory : public IIccTagFactory +{ +public: + virtual CIccTag* CreateTag(icTagTypeSignature tagSig); + + virtual const icChar* GetTagSigName(icTagSignature tagSig); + virtual icTagSignature GetTagNameSig(const icChar *szTagName); + + virtual const icChar* GetTagTypeSigName(icTagTypeSignature tagTypeSig); + virtual icTagTypeSignature GetTagTypeNameSig(const icChar *szTypeName); +}; + +#ifdef USEICCDEVNAMESPACE +} // namespace iccDEV +#endif + +#endif // _ICCTAGJSONFACTORY_H diff --git a/IccJSON/IccLibJSON/IccUtilJson.cpp b/IccJSON/IccLibJSON/IccUtilJson.cpp new file mode 100644 index 000000000..c89d362d1 --- /dev/null +++ b/IccJSON/IccLibJSON/IccUtilJson.cpp @@ -0,0 +1,482 @@ +/** @file + File: IccUtilJson.cpp + + Contains: Implementation of Utilities used for ICC/JSON processing + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#include "IccUtilJson.h" +#include "IccUtil.h" +#include "IccTagLut.h" +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Hex data helpers (self-contained, no IccXML dependency) +// --------------------------------------------------------------------------- + +static int hexVal(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + +icUInt32Number icJsonGetHexData(void *pBuf, const char *szText, icUInt32Number nBufSize) +{ + unsigned char *pDest = (unsigned char*)pBuf; + icUInt32Number rv = 0; + while (rv < nBufSize && *szText) { + int c1 = hexVal(szText[0]); + if (c1 >= 0 && szText[1]) { + int c2 = hexVal(szText[1]); + if (c2 >= 0) { + *pDest++ = (unsigned char)(c1 * 16 + c2); + szText += 2; + rv++; + continue; + } + } + szText++; + } + return rv; +} + +icUInt32Number icJsonGetHexDataSize(const char *szText) +{ + icUInt32Number rv = 0; + while (*szText) { + if (hexVal(szText[0]) >= 0 && szText[1] && hexVal(szText[1]) >= 0) { + szText += 2; + rv++; + } else { + szText++; + } + } + return rv; +} + +std::string icJsonDumpHexData(const void *pBuf, size_t nBufSize) +{ + const unsigned char *p = (const unsigned char*)pBuf; + const size_t bufSize = 3; + char buf[bufSize]; + std::string result; + result.reserve(nBufSize * 2); + for (size_t i = 0; i < nBufSize; i++) { + snprintf(buf, bufSize, "%02x", p[i]); + result += buf; + } + return result; +} + +// --------------------------------------------------------------------------- +// CLUT serialization +// --------------------------------------------------------------------------- + +bool icCLUTDataToJson(IccJson &j, CIccCLUT *pCLUT, icConvertType nType, bool bSaveGridPoints) +{ + if (!pCLUT) + return false; + + icUInt8Number i; + icUInt8Number nInput = pCLUT->GetInputDim(); + icUInt16Number nOutput = pCLUT->GetOutputChannels(); + + if (bSaveGridPoints) { + IccJson gridPts = IccJson::array(); + for (i = 0; i < nInput; i++) + gridPts.push_back((int)pCLUT->GridPoint(i)); + j["gridPoints"] = gridPts; + } + + // Resolve variable precision the same way IccXML does + if (nType == icConvertVariable) + nType = (pCLUT->GetPrecision() == 1) ? icConvert8Bit : icConvert16Bit; + + // Record precision so the parser knows how to interpret the integers + if (nType == icConvert8Bit) + j["precision"] = 1; + else if (nType == icConvert16Bit) + j["precision"] = 2; + + IccJson data = IccJson::array(); + icUInt32Number nTotal = pCLUT->NumPoints() * (icUInt32Number)nOutput; + icFloatNumber *pData = pCLUT->GetData(0); + + for (icUInt32Number k = 0; k < nTotal; k++) { + switch (nType) { + case icConvert8Bit: + data.push_back((int)(pData[k] * 255.0f + 0.5f)); + break; + case icConvert16Bit: + data.push_back((int)(pData[k] * 65535.0f + 0.5f)); + break; + default: + data.push_back((double)pData[k]); + break; + } + } + j["data"] = data; + return true; +} + +bool icCLUTToJson(IccJson &j, CIccCLUT *pCLUT, icConvertType nType, + bool bSaveGridPoints, const char *szName) +{ + IccJson clut; + if (!icCLUTDataToJson(clut, pCLUT, nType, bSaveGridPoints)) + return false; + j[szName] = clut; + return true; +} + +// --------------------------------------------------------------------------- +// Device attribute value (JSON-specific: reads numeric value from JSON node) +// --------------------------------------------------------------------------- + +icUInt64Number icJsonGetDeviceAttrValue(const IccJson &j) +{ + if (j.is_number_unsigned()) + return j.get(); + return 0; +} + +IccJson icJsonGetDeviceAttr(icUInt64Number devAttr) +{ + IccJson j; + j["ReflectiveOrTransparency"] = (devAttr & icTransparency) ? "transparency" : "reflective"; + j["GlossyOrMatte"] = (devAttr & icMatte) ? "matte" : "glossy"; + j["MediaPolarity"] = (devAttr & icMediaNegative) ? "negative" : "positive"; + j["MediaColour"] = (devAttr & icMediaBlackAndWhite) ? "blackAndWhite" : "colour"; + icUInt64Number knownBits = (icUInt64Number)(icTransparency | icMatte | icMediaNegative | icMediaBlackAndWhite); + icUInt64Number other = devAttr & ~knownBits; + if (other) { + char buf[32]; + snprintf(buf, sizeof(buf), "%016llx", (unsigned long long)other); + j["VendorSpecific"] = buf; + } + return j; +} + +icUInt64Number icJsonParseDeviceAttr(const IccJson &j) +{ + icUInt64Number attr = 0; + std::string s; + jGetString(j, "ReflectiveOrTransparency", s); if (s == "transparency") attr |= icTransparency; + s.clear(); jGetString(j, "GlossyOrMatte", s); if (s == "matte") attr |= icMatte; + s.clear(); jGetString(j, "MediaPolarity", s); if (s == "negative") attr |= icMediaNegative; + s.clear(); jGetString(j, "MediaColour", s); if (s == "blackAndWhite") attr |= icMediaBlackAndWhite; + s.clear(); jGetString(j, "VendorSpecific", s); + if (!s.empty()) { + unsigned long long vendor = 0; + sscanf(s.c_str(), "%llx", &vendor); + attr |= (icUInt64Number)vendor; + } + return attr; +} + +// --------------------------------------------------------------------------- +// MPE element type name / signature lookup +// --------------------------------------------------------------------------- + +struct SIccElemTypeName { + icElemTypeSignature sig; + const icChar *name; +}; + +static const SIccElemTypeName g_IccElemTypeNames[] = { + { icSigCurveSetElemType, "CurveSetElement" }, + { icSigMatrixElemType, "MatrixElement" }, + { icSigCLutElemType, "CLutElement" }, + { icSigExtCLutElemType, "ExtCLutElement" }, + { icSigBAcsElemType, "BAcsElement" }, + { icSigEAcsElemType, "EAcsElement" }, + { icSigCalculatorElemType, "CalculatorElement" }, + { icSigXYZToJabElemType, "XYZToJabElement" }, + { icSigJabToXYZElemType, "JabToXYZElement" }, + { icSigTintArrayElemType, "TintArrayElement" }, + { icSigToneMapElemType, "ToneMapElement" }, + { icSigEmissionMatrixElemType, "EmissionMatrixElement" }, + { icSigInvEmissionMatrixElemType, "InvEmissionMatrixElement" }, + { icSigEmissionCLUTElemType, "EmissionCLutElement" }, + { icSigReflectanceCLUTElemType, "ReflectanceCLutElement" }, + { icSigEmissionObserverElemType, "EmissionObserverElement" }, + { icSigReflectanceObserverElemType, "ReflectanceObserverElement" }, + { icSigSparseMatrixElemType, "SparseMatrixElement" }, + { icSigUnknownElemType, "UnknownElement" }, + { (icElemTypeSignature)0, NULL } +}; + +const icChar* icJsonGetElemTypeName(icElemTypeSignature sig) +{ + for (int i = 0; g_IccElemTypeNames[i].name; i++) { + if (g_IccElemTypeNames[i].sig == sig) + return g_IccElemTypeNames[i].name; + } + return "UnknownElement"; +} + +icElemTypeSignature icJsonGetElemTypeNameSig(const icChar *szName) +{ + if (!szName) + return icSigUnknownElemType; + for (int i = 0; g_IccElemTypeNames[i].name; i++) { + if (!strcmp(g_IccElemTypeNames[i].name, szName)) + return g_IccElemTypeNames[i].sig; + } + // Fall back to 4-char signature parsing + return (icElemTypeSignature)icGetSigVal(szName); +} + +// --------------------------------------------------------------------------- +// JSON value extraction helpers +// --------------------------------------------------------------------------- + +template +bool jsonToValue(const IccJson &j, T &value) +{ + if (j.is_number()) { + value = j.get(); + return true; + } + return false; +} + +template <> +bool jsonToValue(const IccJson &j, bool &value) +{ + if (j.is_boolean()) { value = j.get(); return true; } + if (j.is_number_integer()) { value = j.get() != 0; return true; } + if (j.is_number_unsigned()) { value = j.get() != 0; return true; } + if (j.is_number_float()) { value = j.get() > 0.5f; return true; } + return false; +} + +bool jsonToString(const IccJson &j, std::string &value) +{ + if (j.is_string()) { value = j.get(); return true; } + return false; +} + +template <> +bool jsonToValue(const IccJson &j, std::string &value) +{ + return jsonToString(j, value); +} + +template +bool jsonToArray(const IccJson &j, T *vals, int n) +{ + if (!j.is_array()) return false; + int nValid = 0; + for (int i = 0; i < n && i < (int)j.size(); i++) { + if (j[i].is_number()) { vals[i] = j[i].get(); nValid++; } + } + return nValid == n; +} + +template +bool jsonToArray(const IccJson &j, std::vector &vals) +{ + if (!j.is_array()) return false; + vals.resize(j.size()); + int nValid = 0; + for (int i = 0; i < (int)j.size(); i++) { + if (j[i].is_number()) { vals[i] = j[i].get(); nValid++; } + else vals[i] = T(0); + } + return nValid == (int)j.size(); +} + +bool jsonExistsField(const IccJson &j, const char *field) +{ + return j.is_object() && j.contains(field); +} + +template +bool jGetValue(const IccJson &j, const char *field, T &value) +{ + if (!j.is_object() || !j.contains(field)) return false; + return jsonToValue(j[field], value); +} + +bool jGetString(const IccJson &j, const char *field, std::string &value) +{ + if (!j.is_object() || !j.contains(field)) return false; + return jsonToString(j[field], value); +} + +template +bool jGetArray(const IccJson &j, const char *field, T *vals, int n) +{ + if (!j.is_object() || !j.contains(field)) return false; + return jsonToArray(j[field], vals, n); +} + +// Explicit instantiations +// On Linux/macOS icUInt32Number is uint32_t (same as unsigned int), but on +// Windows/MSVC it is unsigned long. Instantiate unsigned int only on MSVC +// to avoid duplicate-symbol errors on other platforms. +template bool jsonToValue(const IccJson&, int&); +template bool jsonToValue(const IccJson&, icUInt8Number&); +template bool jsonToValue(const IccJson&, icUInt16Number&); +template bool jsonToValue(const IccJson&, icUInt32Number&); +template bool jsonToValue(const IccJson&, icInt16Number&); +template bool jsonToValue(const IccJson&, icFloat32Number&); +template bool jsonToValue(const IccJson&, icFloat64Number&); +#ifdef _MSC_VER +template bool jsonToValue(const IccJson&, unsigned int&); +#endif + +template bool jsonToArray(const IccJson&, double*, int); +template bool jsonToArray(const IccJson&, float*, int); +template bool jsonToArray(const IccJson&, icUInt8Number*, int); +template bool jsonToArray(const IccJson&, icUInt16Number*, int); +template bool jsonToArray(const IccJson&, icUInt32Number*, int); +template bool jsonToArray(const IccJson&, std::vector&); +template bool jsonToArray(const IccJson&, std::vector&); + +template bool jGetValue(const IccJson&, const char*, int&); +template bool jGetValue(const IccJson&, const char*, icUInt8Number&); +template bool jGetValue(const IccJson&, const char*, icUInt16Number&); +template bool jGetValue(const IccJson&, const char*, icUInt32Number&); +template bool jGetValue(const IccJson&, const char*, icInt16Number&); +template bool jGetValue(const IccJson&, const char*, icFloat32Number&); +template bool jGetValue(const IccJson&, const char*, icFloat64Number&); +template bool jGetValue(const IccJson&, const char*, bool&); +#ifdef _MSC_VER +template bool jGetValue(const IccJson&, const char*, unsigned int&); +#endif + +template bool jGetArray(const IccJson&, const char*, double*, int); +template bool jGetArray(const IccJson&, const char*, float*, int); +template bool jGetArray(const IccJson&, const char*, icUInt8Number*, int); +template bool jGetArray(const IccJson&, const char*, icUInt16Number*, int); +template bool jGetArray(const IccJson&, const char*, icUInt32Number*, int); + +// --------------------------------------------------------------------------- +// CIccJsonArrayType template implementations +// --------------------------------------------------------------------------- + +template +CIccJsonArrayType::CIccJsonArrayType() + : m_nSize(0), m_pBuf(NULL) +{} + +template +CIccJsonArrayType::~CIccJsonArrayType() +{ + delete[] m_pBuf; +} + +template +bool CIccJsonArrayType::SetSize(icUInt32Number nSize) +{ + if (m_pBuf) { delete[] m_pBuf; m_pBuf = NULL; } + m_nSize = nSize; + if (nSize) { + m_pBuf = new T[nSize]; + if (!m_pBuf) { m_nSize = 0; return false; } + } + return true; +} + +template +bool CIccJsonArrayType::DumpArray(IccJson &j, const T *buf, + icUInt32Number nBufSize, + icConvertType /*nType*/) +{ + IccJson arr = IccJson::array(); + for (icUInt32Number i = 0; i < nBufSize; i++) + arr.push_back(buf[i]); + j = arr; + return true; +} + +template +bool CIccJsonArrayType::ParseArray(T *buf, icUInt32Number nBufSize, + const IccJson &j) +{ + if (!j.is_array()) return false; + icUInt32Number n = (icUInt32Number)j.size(); + if (n > nBufSize) n = nBufSize; + for (icUInt32Number i = 0; i < n; i++) + buf[i] = j[i].get(); + return true; +} + +template +bool CIccJsonArrayType::ParseArray(const IccJson &j) +{ + if (!j.is_array()) return false; + icUInt32Number n = (icUInt32Number)j.size(); + if (!SetSize(n)) return false; + for (icUInt32Number i = 0; i < n; i++) + m_pBuf[i] = j[i].get(); + return true; +} + +// Explicit instantiations for all used types +template class CIccJsonArrayType; +template class CIccJsonArrayType; +template class CIccJsonArrayType; +template class CIccJsonArrayType; +template class CIccJsonArrayType; +template class CIccJsonArrayType; +template class CIccJsonArrayType; diff --git a/IccJSON/IccLibJSON/IccUtilJson.h b/IccJSON/IccLibJSON/IccUtilJson.h new file mode 100644 index 000000000..4c769fc20 --- /dev/null +++ b/IccJSON/IccLibJSON/IccUtilJson.h @@ -0,0 +1,178 @@ +/** @file + File: IccUtilJson.h + + Contains: Header for Utilities used for ICC/JSON processing + + Version: V1 + + Copyright: (c) see Software License +*/ + +/* + * Copyright (c) International Color Consortium. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. In the absence of prior written permission, the names "ICC" and "The + * International Color Consortium" must not be used to imply that the + * ICC organization endorses or promotes products derived from this + * software. + * + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR + * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the The International Color Consortium. + * + * + * Membership in the ICC is encouraged when this software is used for + * commercial purposes. + * + * + * For more information on The International Color Consortium, please + * see . + * + * + */ + +#ifndef _ICCUTILJSON_H +#define _ICCUTILJSON_H + +#include "IccUtil.h" +#include "IccTag.h" +#include "IccJsonConfig.h" +#include +#include +#include + +using IccJson = nlohmann::json; + +// --------------------------------------------------------------------------- +// Hex data helpers (binary blobs stored as hex strings in JSON) +// --------------------------------------------------------------------------- +icUInt32Number icJsonGetHexData(void *pBuf, const char *szText, icUInt32Number nBufSize); +icUInt32Number icJsonGetHexDataSize(const char *szText); +std::string icJsonDumpHexData(const void *pBuf, size_t nBufSize); + +// --------------------------------------------------------------------------- +// CLUT serialization +// --------------------------------------------------------------------------- +bool icCLUTDataToJson(IccJson &j, CIccCLUT *pCLUT, icConvertType nType, bool bSaveGridPoints = false); +bool icCLUTToJson(IccJson &j, CIccCLUT *pCLUT, icConvertType nType, + bool bSaveGridPoints = false, const char *szName = "CLUT"); +CIccCLUT *icCLUTFromJson(const IccJson &j, int nIn, int nOut, + icConvertType nType, std::string &parseStr); + +// --------------------------------------------------------------------------- +// Device attribute helpers +// --------------------------------------------------------------------------- +icUInt64Number icJsonGetDeviceAttrValue(const IccJson &j); +IccJson icJsonGetDeviceAttr(icUInt64Number devAttr); +icUInt64Number icJsonParseDeviceAttr(const IccJson &j); + +// --------------------------------------------------------------------------- +// MPE element type name / signature lookup +// --------------------------------------------------------------------------- +const icChar* icJsonGetElemTypeName(icElemTypeSignature elemTypeSig); +icElemTypeSignature icJsonGetElemTypeNameSig(const icChar *szElemType); + +// --------------------------------------------------------------------------- +// JSON value extraction helpers (ported from IccCommon/IccJsonUtil) +// --------------------------------------------------------------------------- + +// Extract a typed numeric value from a JSON node. +// Returns true if the node has a compatible type and value was assigned. +template +bool jsonToValue(const IccJson &j, T &value); + +template <> +bool jsonToValue(const IccJson &j, bool &value); + +template <> +bool jsonToValue(const IccJson &j, std::string &value); + +// Extract a string value from a JSON node. +bool jsonToString(const IccJson &j, std::string &value); + +// Extract a fixed-size C array from a JSON array node. +// Returns true only when all n elements were found and numeric. +template +bool jsonToArray(const IccJson &j, T *vals, int n); + +// Extract a variable-length array into a std::vector. +template +bool jsonToArray(const IccJson &j, std::vector &vals); + +// True when a named field exists inside a JSON object. +bool jsonExistsField(const IccJson &j, const char *field); + +// Field-level convenience: combines j.contains(field) with jsonToValue. +// Returns true if the field was present and the value was successfully assigned. +template +bool jGetValue(const IccJson &j, const char *field, T &value); + +// Field-level convenience for strings. +bool jGetString(const IccJson &j, const char *field, std::string &value); + +// Field-level convenience for fixed-size arrays. +template +bool jGetArray(const IccJson &j, const char *field, T *vals, int n); + +// --------------------------------------------------------------------------- +// Typed array helpers (parallel to CIccXmlArrayType) +// --------------------------------------------------------------------------- +template +class CIccJsonArrayType +{ +public: + CIccJsonArrayType(); + ~CIccJsonArrayType(); + + static bool DumpArray(IccJson &j, const T *buf, icUInt32Number nBufSize, + icConvertType nType); + static bool ParseArray(T *buf, icUInt32Number nBufSize, const IccJson &j); + + bool ParseArray(const IccJson &j); + bool SetSize(icUInt32Number nSize); + + T *GetBuf() { return m_pBuf; } + icUInt32Number GetSize() { return m_nSize; } + +protected: + icUInt32Number m_nSize; + T *m_pBuf; +}; + +#define icSigJsonFloatArrayType ((icTagTypeSignature)0x66637420) /* 'flt ' */ + +typedef CIccJsonArrayType CIccJsonUInt8Array; +typedef CIccJsonArrayType CIccJsonUInt16Array; +typedef CIccJsonArrayType CIccJsonUInt32Array; +typedef CIccJsonArrayType CIccJsonUInt64Array; +typedef CIccJsonArrayType CIccJsonFloatArray; +typedef CIccJsonArrayType CIccJsonFloat32Array; +typedef CIccJsonArrayType CIccJsonFloat64Array; + +#endif // _ICCUTILJSON_H diff --git a/IccProfLib/IccCmm.cpp b/IccProfLib/IccCmm.cpp index 0065e09d5..5728fbb99 100644 --- a/IccProfLib/IccCmm.cpp +++ b/IccProfLib/IccCmm.cpp @@ -959,7 +959,7 @@ CIccXform *CIccXform::Create(CIccProfile *pProfile, case icXformLutBRDFMcsParam: { CIccTag *pTag = NULL; - if (pProfile->m_Header.deviceClass == icSigMaterialVisualizationClass) { + if (pProfile->m_Header.deviceClass == icSigMultiplexVisualizationClass) { bInput = true; nMCS = icFromMCS; @@ -1045,13 +1045,13 @@ CIccXform *CIccXform::Create(CIccProfile *pProfile, case icXformLutMCS: { CIccTag *pTag = NULL; - if (pProfile->m_Header.deviceClass==icSigMaterialIdentificationClass || + if (pProfile->m_Header.deviceClass==icSigMultiplexIdentificationClass || pProfile->m_Header.deviceClass==icSigInputClass) { bInput = true; nMCS = icToMCS; pTag = pProfile->FindTag(icSigAToM0Tag); } - else if (pProfile->m_Header.deviceClass==icSigMaterialVisualizationClass || + else if (pProfile->m_Header.deviceClass==icSigMultiplexVisualizationClass || pProfile->m_Header.deviceClass==icSigOutputClass) { bInput = true; nMCS = icFromMCS; @@ -1124,7 +1124,7 @@ CIccXform *CIccXform::Create(CIccProfile *pProfile, if (pTag && !pTag->IsSupported()) pTag = NULL; } - else if (pProfile->m_Header.deviceClass==icSigMaterialLinkClass) { + else if (pProfile->m_Header.deviceClass==icSigMultiplexLinkClass) { bInput = false; nMCS = icFromMCS; pTag = pProfile->FindTag(icSigMToA0Tag); @@ -8189,9 +8189,9 @@ icStatusCMM CIccCmm::AddXform(CIccProfile *pProfile, return icCmmStatInvalidProfile; switch(pProfile->m_Header.deviceClass) { - case icSigMaterialIdentificationClass: - case icSigMaterialVisualizationClass: - case icSigMaterialLinkClass: + case icSigMultiplexIdentificationClass: + case icSigMultiplexVisualizationClass: + case icSigMultiplexLinkClass: nIntent = icPerceptual; nLutType = icXformLutMCS; break; @@ -8305,7 +8305,7 @@ icStatusCMM CIccCmm::AddXform(CIccProfile *pProfile, } nSrcSpace = (icColorSpaceSignature)pProfile->m_Header.mcs; - if (pProfile->m_Header.deviceClass==icSigMaterialVisualizationClass || + if (pProfile->m_Header.deviceClass==icSigMultiplexVisualizationClass || pProfile->m_Header.deviceClass==icSigOutputClass) { if (bUseD2BxB2DxTags && pProfile->m_Header.spectralPCS) { nDstSpace = (icColorSpaceSignature)pProfile->m_Header.spectralPCS; @@ -8314,7 +8314,7 @@ icStatusCMM CIccCmm::AddXform(CIccProfile *pProfile, nDstSpace = pProfile->m_Header.pcs; } } - else if (pProfile->m_Header.deviceClass==icSigMaterialLinkClass) { + else if (pProfile->m_Header.deviceClass==icSigMultiplexLinkClass) { nDstSpace = pProfile->m_Header.colorSpace; nParentSpace = pProfile->GetParentColorSpace(); } @@ -8359,7 +8359,7 @@ icStatusCMM CIccCmm::AddXform(CIccProfile *pProfile, } // this must check before creating the Xform, because that can delete the profile - if (pProfile->m_Header.deviceClass == icSigMaterialVisualizationClass) { + if (pProfile->m_Header.deviceClass == icSigMultiplexVisualizationClass) { bInput = true; } @@ -8419,9 +8419,9 @@ icStatusCMM CIccCmm::AddXform(CIccProfile *pProfile, return icCmmStatInvalidProfile; switch (pProfile->m_Header.deviceClass) { - case icSigMaterialIdentificationClass: - case icSigMaterialVisualizationClass: - case icSigMaterialLinkClass: + case icSigMultiplexIdentificationClass: + case icSigMultiplexVisualizationClass: + case icSigMultiplexLinkClass: return icCmmStatBadLutType; default: @@ -10613,13 +10613,13 @@ icStatusCMM CIccNamedColorCmm::AddXform(CIccProfile *pProfile, icXformLutType nUseLutType = nLutType; switch(pProfile->m_Header.deviceClass) { - case icSigMaterialIdentificationClass: - case icSigMaterialLinkClass: + case icSigMultiplexIdentificationClass: + case icSigMultiplexLinkClass: nIntent = icPerceptual; nLutType = icXformLutMCS; break; - case icSigMaterialVisualizationClass: + case icSigMultiplexVisualizationClass: nLutType = icXformLutMCS; break; @@ -10758,11 +10758,11 @@ icStatusCMM CIccNamedColorCmm::AddXform(CIccProfile *pProfile, switch(pProfile->m_Header.deviceClass) { case icSigInputClass: - case icSigMaterialIdentificationClass: + case icSigMultiplexIdentificationClass: nSrcSpace = pProfile->m_Header.colorSpace; nDstSpace = (icColorSpaceSignature)pProfile->m_Header.mcs; break; - case icSigMaterialVisualizationClass: + case icSigMultiplexVisualizationClass: nSrcSpace = (icColorSpaceSignature)pProfile->m_Header.mcs; if (bUseD2BxB2DxTags && pProfile->m_Header.spectralPCS) { nDstSpace = (icColorSpaceSignature)pProfile->m_Header.spectralPCS; @@ -10773,7 +10773,7 @@ icStatusCMM CIccNamedColorCmm::AddXform(CIccProfile *pProfile, bInput = true; break; - case icSigMaterialLinkClass: + case icSigMultiplexLinkClass: nSrcSpace = (icColorSpaceSignature)pProfile->m_Header.mcs; nDstSpace = pProfile->m_Header.colorSpace; break; diff --git a/IccProfLib/IccProfile.cpp b/IccProfLib/IccProfile.cpp index f7e68d1c9..cef60adb6 100644 --- a/IccProfLib/IccProfile.cpp +++ b/IccProfLib/IccProfile.cpp @@ -1489,9 +1489,9 @@ icValidateStatus CIccProfile::CheckHeader(std::string &sReport, const CIccProfil break; case icSigColorEncodingClass: - case icSigMaterialIdentificationClass: - case icSigMaterialVisualizationClass: - case icSigMaterialLinkClass: + case icSigMultiplexIdentificationClass: + case icSigMultiplexVisualizationClass: + case icSigMultiplexLinkClass: if (m_Header.version=icVersionNumberV5 && m_Header.deviceClass==icSigAbstractClass && Info.IsValidSpectralSpace(m_Header.colorSpace) && IsTagPresent(icSigDToB0Tag))) { sReport += icMsgValidateCriticalError; @@ -1540,8 +1540,8 @@ icValidateStatus CIccProfile::CheckHeader(std::string &sReport, const CIccProfil } } - if (m_Header.deviceClass==icSigMaterialIdentificationClass || - m_Header.deviceClass==icSigMaterialLinkClass) { + if (m_Header.deviceClass==icSigMultiplexIdentificationClass || + m_Header.deviceClass==icSigMultiplexLinkClass) { if (m_Header.pcs!=icSigNoColorData) { sReport += icMsgValidateNonCompliant; snprintf(buf, bufSize, "Invalid PCS designator for %s\n", Info.GetProfileClassSigName(m_Header.deviceClass)); @@ -1934,9 +1934,9 @@ bool CIccProfile::CheckTagExclusion(std::string &sReport) const break; } - case icSigMaterialIdentificationClass: - case icSigMaterialLinkClass: - case icSigMaterialVisualizationClass: + case icSigMultiplexIdentificationClass: + case icSigMultiplexLinkClass: + case icSigMultiplexVisualizationClass: { if (GetTag(icSigAToB0Tag) || GetTag(icSigAToB1Tag) || GetTag(icSigAToB2Tag) || GetTag(icSigBToA0Tag) || GetTag(icSigBToA1Tag) || GetTag(icSigBToA2Tag) || @@ -2550,7 +2550,7 @@ icValidateStatus CIccProfile::CheckRequiredTags(std::string &sReport, const CIcc rv = icMaxStatus(rv, icValidateNonCompliant); } - if (sig != icSigLinkClass && sig != icSigMaterialIdentificationClass && sig != icSigMaterialLinkClass) { + if (sig != icSigLinkClass && sig != icSigMultiplexIdentificationClass && sig != icSigMultiplexLinkClass) { if ((m_Header.version 0 && nChan <= 0xFFFF) return "NChannel"; - // v5/iccMAX: MCS (0x6d63xxxx) — material channel set + // v5/iccMAX: MCS (0x6d63xxxx) — multiplex channel set if (csType == 0x6d630000 && nChan > 0 && nChan <= 0xFFFF) return "MCS"; diff --git a/IccProfLib/IccTagBasic.cpp b/IccProfLib/IccTagBasic.cpp index 72d3d0bd8..6f9e2e475 100644 --- a/IccProfLib/IccTagBasic.cpp +++ b/IccProfLib/IccTagBasic.cpp @@ -5986,13 +5986,13 @@ icValidateStatus CIccTagNum::Validate(std::string sigPath, std::string //Check # of channels if (icGetFirstSigPathSig(sigPath) == icSigMultiplexDefaultValuesTag && pProfile && - m_nSize != icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs)) { + m_nSize != icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs)) { CIccInfo Info; std::string sSigPathName = Info.GetSigPathName(sigPath); sReport += icMsgValidateCriticalError; sReport += sSigPathName; - sReport += " - Number of material default values does not match MCS in header.\n"; + sReport += " - Number of multiplex default values does not match MCS in header.\n"; rv = icMaxStatus(rv, icValidateCriticalError); } @@ -6677,13 +6677,13 @@ icValidateStatus CIccTagFloatNum::Validate(std::string sigPath, std::st //Check # of channels if (icGetFirstSigPathSig(sigPath) == icSigMultiplexDefaultValuesTag && pProfile && - m_nSize != icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs)) { + m_nSize != icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs)) { CIccInfo Info; std::string sSigPathName = Info.GetSigPathName(sigPath); sReport += icMsgValidateCriticalError; sReport += sSigPathName; - sReport += " - Number of material default values does not match MCS in header.\n"; + sReport += " - Number of multiplex default values does not match MCS in header.\n"; rv = icMaxStatus(rv, icValidateCriticalError); } @@ -9414,7 +9414,7 @@ bool CIccProfileDescText::SetType(icTagTypeSignature nType) if (nType == icSigMultiLocalizedUnicodeType || nType == icSigTextDescriptionType) - m_pTag = CIccTag::Create(nType); + m_pTag = CIccTagCreator::CreateTag(nType); else m_pTag = NULL; diff --git a/IccProfLib/IccTagComposite.cpp b/IccProfLib/IccTagComposite.cpp index a9375e707..c159356ef 100644 --- a/IccProfLib/IccTagComposite.cpp +++ b/IccProfLib/IccTagComposite.cpp @@ -1483,12 +1483,12 @@ icValidateStatus CIccTagArray::Validate(std::string sigPath, std::string &sRepor //Check # of channels if (icGetFirstSigPathSig(sigPath) == icSigMultiplexTypeArrayTag && pProfile && - m_nSize != icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs)) { + m_nSize != icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs)) { std::string sSigPathName = Info.GetSigPathName(sigPath); sReport += icMsgValidateCriticalError; sReport += sSigPathName; - sReport += " - Number of material channel names does not match MCS in header.\n"; + sReport += " - Number of multiplex channel names does not match MCS in header.\n"; rv = icMaxStatus(rv, icValidateCriticalError); } icUInt32Number i; diff --git a/IccProfLib/IccTagFactory.cpp b/IccProfLib/IccTagFactory.cpp index e6d8d3e04..401bf0d25 100644 --- a/IccProfLib/IccTagFactory.cpp +++ b/IccProfLib/IccTagFactory.cpp @@ -219,6 +219,7 @@ icSigNamePair g_icTagNameTable[] = { }; icSigNamePair g_icAltTagNameTable[] = { + // Legacy names for backward compatibility with older XML/JSON files {icSigMultiplexTypeArrayTag, "materialTypeArrayTag"}, {icSigMultiplexDefaultValuesTag, "materialDefaultValuesTag"}, {(icTagSignature)0,""}, diff --git a/IccProfLib/IccTagMPE.cpp b/IccProfLib/IccTagMPE.cpp index 4bf6b3fa0..965859582 100644 --- a/IccProfLib/IccTagMPE.cpp +++ b/IccProfLib/IccTagMPE.cpp @@ -1755,7 +1755,7 @@ icValidateStatus CIccTagMultiProcessElement::Validate(std::string sigPath, std:: case icSigMToA0Tag: { - nInput = icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs); + nInput = icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs); if (m_nInputChannels != nInput) { sReport += icMsgValidateCriticalError; sReport += sSigPathName; @@ -1784,7 +1784,7 @@ icValidateStatus CIccTagMultiProcessElement::Validate(std::string sigPath, std:: rv = icMaxStatus(rv, icValidateCriticalError); } - nOutput = icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs); + nOutput = icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs); if (m_nOutputChannels != nOutput) { sReport += icMsgValidateCriticalError; sReport += sSigPathName; @@ -1800,7 +1800,7 @@ icValidateStatus CIccTagMultiProcessElement::Validate(std::string sigPath, std:: case icSigMToB2Tag: case icSigMToB3Tag: { - nInput = icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs); + nInput = icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs); if (m_nInputChannels != nInput) { sReport += icMsgValidateCriticalError; sReport += sSigPathName; @@ -1824,7 +1824,7 @@ icValidateStatus CIccTagMultiProcessElement::Validate(std::string sigPath, std:: case icSigMToS2Tag: case icSigMToS3Tag: { - nInput = icGetMaterialColorSpaceSamples(pProfile->m_Header.mcs); + nInput = icGetMultiplexColorSpaceSamples(pProfile->m_Header.mcs); if (m_nInputChannels != nInput) { sReport += icMsgValidateCriticalError; sReport += sSigPathName; diff --git a/IccProfLib/IccUtil.cpp b/IccProfLib/IccUtil.cpp index 4461e7754..69a4365b0 100644 --- a/IccProfLib/IccUtil.cpp +++ b/IccProfLib/IccUtil.cpp @@ -75,6 +75,7 @@ #include "IccArrayFactory.h" #include "IccMpeFactory.h" #include "IccDefs.h" +#include "IccConvertUTF.h" #include #include #include @@ -1460,7 +1461,7 @@ icUInt8Number icGetStorageTypeBytes(icUInt16Number nStorageType) } -icUInt32Number icGetMaterialColorSpaceSamples(icMaterialColorSignature sig) +icUInt32Number icGetMultiplexColorSpaceSamples(icMultiplexColorSignature sig) { if (icGetColorSpaceType((icColorSpaceSignature)sig)!=icSigSrcMCSChannelData) return 0; @@ -1900,14 +1901,14 @@ const icChar *CIccInfo::GetProfileClassSigName(icProfileClassSignature sig) case icSigColorEncodingClass: return "ColorEncodingClass"; - case icSigMaterialIdentificationClass: - return "MaterialIdentificationClass"; + case icSigMultiplexIdentificationClass: + return "MultiplexIdentificationClass"; - case icSigMaterialVisualizationClass: - return "MaterialVisualizationClass"; + case icSigMultiplexVisualizationClass: + return "MultiplexVisualizationClass"; - case icSigMaterialLinkClass: - return "MaterialLinkClass"; + case icSigMultiplexLinkClass: + return "MultiplexLinkClass"; default: return GetUnknownName(sig); @@ -2691,6 +2692,315 @@ CIccPixelBuf::~CIccPixelBuf() delete [] m_pixel; } +// =========================================================================== +// CIccUTF16String (moved from IccUtilXml for shared use by IccJSON) +// =========================================================================== + +CIccUTF16String::CIccUTF16String() +{ + m_alloc = 64; + m_len = 0; + m_str = (icUInt16Number*)calloc(m_alloc, sizeof(icUInt16Number)); + if (!m_str) { + m_alloc = 0; + } +} + +CIccUTF16String::CIccUTF16String(const icUInt16Number *uzStr) +{ + m_len = WStrlen(uzStr); + m_alloc = AllocSize(m_len); + m_str = (icUInt16Number*)malloc(m_alloc * sizeof(icUInt16Number)); + if (!m_str) { + m_alloc = 0; + m_len = 0; + return; + } + memcpy(m_str, uzStr, (m_len + 1) * sizeof(icUInt16Number)); +} + +CIccUTF16String::CIccUTF16String(const char *szStr) +{ + size_t sizeSrc = strlen(szStr); + if (sizeSrc) { + m_alloc = AllocSize(sizeSrc * 2); + m_str = (UTF16*)calloc(m_alloc, sizeof(icUInt16Number)); + if (!m_str) { + m_alloc = 0; + m_len = 0; + return; + } + UTF16 *szDest = m_str; + icConvertUTF8toUTF16((const UTF8**)&szStr, (const UTF8*)&szStr[sizeSrc], &szDest, &szDest[m_alloc], lenientConversion); + if (m_str[0] == 0xfeff) { + size_t i; + for (i = 1; m_str[i]; i++) m_str[i-1] = m_str[i]; + m_str[i-1] = 0; + } + m_len = WStrlen(m_str); + } else { + m_alloc = 64; + m_len = 0; + m_str = (icUInt16Number*)calloc(m_alloc, sizeof(icUInt16Number)); + if (!m_str) { + m_alloc = 0; + } + } +} + +CIccUTF16String::CIccUTF16String(const CIccUTF16String &str) +{ + m_alloc = str.m_alloc; + m_len = str.m_len; + m_str = (icUInt16Number*)malloc(m_alloc * sizeof(icUInt16Number)); + if (!m_str) { + m_alloc = 0; + m_len = 0; + return; + } + memcpy(m_str, str.m_str, m_alloc * sizeof(icUInt16Number)); +} + +CIccUTF16String::~CIccUTF16String() +{ + free(m_str); +} + +void CIccUTF16String::Clear() +{ + m_len = 0; + m_str[0] = 0; +} + +bool CIccUTF16String::Resize(size_t len) +{ + if (len > m_alloc) { + size_t nAlloc = AllocSize(len); + m_str = (icUInt16Number*)icRealloc(m_str, nAlloc * sizeof(icUInt16Number)); + if (!m_str) { m_len = 0; return false; } + m_alloc = nAlloc; + } + if (len > m_len) + memset(&m_str[m_len], 0x0020, (len - m_len) * sizeof(icUInt16Number)); + m_len = len; + m_str[m_len] = 0; + return true; +} + +size_t CIccUTF16String::WStrlen(const icUInt16Number *uzStr) +{ + size_t n = 0; + while (uzStr[n]) n++; + return n; +} + +CIccUTF16String& CIccUTF16String::operator=(const CIccUTF16String &wstr) +{ + if (m_alloc <= wstr.m_alloc) { + m_str = (icUInt16Number*)icRealloc(m_str, wstr.m_alloc * sizeof(icUInt16Number)); + m_alloc = m_str ? wstr.m_alloc : 0; + } + if (m_str) { + m_len = wstr.m_len; + memcpy(m_str, wstr.m_str, (m_len + 1) * sizeof(icUInt16Number)); + } else { + m_len = 0; + } + return *this; +} + +CIccUTF16String& CIccUTF16String::operator=(const char *szStr) +{ + FromUtf8(szStr, 0); + return *this; +} + +CIccUTF16String& CIccUTF16String::operator=(const icUInt16Number *uzStr) +{ + size_t n = WStrlen(uzStr); + size_t nAlloc = AllocSize(n); + if (m_alloc <= nAlloc) { + m_str = (icUInt16Number*)icRealloc(m_str, nAlloc * sizeof(icUInt16Number)); + m_alloc = m_str ? nAlloc : 0; + } + if (m_str) { + m_len = n; + memcpy(m_str, uzStr, (m_len + 1) * sizeof(icUInt16Number)); + } else { + m_len = 0; + } + return *this; +} + +bool CIccUTF16String::FromUtf8(const char *szStr, size_t sizeSrc) +{ + if (!sizeSrc) sizeSrc = strlen(szStr); + if (sizeSrc) { + size_t nAlloc = AllocSize(sizeSrc * 2); + if (m_alloc <= nAlloc) { + m_str = (icUInt16Number*)icRealloc(m_str, nAlloc * sizeof(icUInt16Number)); + m_alloc = nAlloc; + } + if (m_str) { + memset(m_str, 0, m_alloc * sizeof(icUInt16Number)); + UTF16 *szDest = m_str; + icConvertUTF8toUTF16((const UTF8**)&szStr, (const UTF8*)&szStr[sizeSrc], &szDest, &szDest[m_alloc], lenientConversion); + if (m_str[0] == 0xfeff) { + size_t i; + for (i = 1; m_str[i]; i++) m_str[i-1] = m_str[i]; + m_str[i-1] = 0; + } + m_len = WStrlen(m_str); + } else { + m_len = 0; + return false; + } + } else { + m_len = 0; + m_str[0] = 0; + } + return true; +} + +const char *CIccUTF16String::ToUtf8(std::string &buf) +{ + return icUtf16ToUtf8(buf, m_str, (int)m_len); +} + +const wchar_t *CIccUTF16String::ToWString(std::wstring &buf) +{ + buf.clear(); + for (size_t i = 0; i < m_len; i++) + buf += (wchar_t)m_str[i]; + return buf.c_str(); +} + +const char *icUtf16ToUtf8(std::string &buf, const icUInt16Number *szSrc, int sizeSrc) +{ + if (!sizeSrc) + sizeSrc = (int)CIccUTF16String::WStrlen(szSrc); + int n = sizeSrc * 4; + if (n) { + char *szBuf = (char*)malloc(n + 1); + char *szDest = szBuf; + icConvertUTF16toUTF8(&szSrc, &szSrc[sizeSrc], (UTF8**)&szDest, (UTF8*)&szDest[n + 1], lenientConversion); + *szDest = '\0'; + buf = szBuf; + free(szBuf); + } else { + buf.clear(); + } + return buf.c_str(); +} + +const unsigned short *icUtf8ToUtf16(CIccUTF16String &buf, const char *szSrc, int sizeSrc) +{ + buf.FromUtf8(szSrc, sizeSrc); + return buf.c_str(); +} + +// --------------------------------------------------------------------------- +// Date/time and rendering intent string parsing +// --------------------------------------------------------------------------- + +icDateTimeNumber icGetDateTimeValue(const icChar *str) +{ + unsigned int day=0, month=0, year=0, hours=0, minutes=0, seconds=0; + icDateTimeNumber dateTime = {}; + + if (!stricmp(str, "now")) { + time_t rawtime; + struct tm *timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + year = timeinfo->tm_year + 1900; + month = timeinfo->tm_mon + 1; + day = timeinfo->tm_mday; + hours = timeinfo->tm_hour; + minutes = timeinfo->tm_min; + seconds = timeinfo->tm_sec; + } else { + sscanf(str, "%d-%02d-%02dT%02d:%02d:%02d", &year, &month, &day, &hours, &minutes, &seconds); + } + dateTime.year = year; + dateTime.month = month; + dateTime.day = day; + dateTime.hours = hours; + dateTime.minutes = minutes; + dateTime.seconds = seconds; + return dateTime; +} + +icRenderingIntent icGetRenderingIntentValue(const icChar *szRenderingIntent) +{ + if (!strcmp(szRenderingIntent, "Perceptual")) + return icPerceptual; + if (!strcmp(szRenderingIntent, "Media-relative colorimetric") || + !strcmp(szRenderingIntent, "Relative") || + !strcmp(szRenderingIntent, "Relative Colorimetric")) + return icRelativeColorimetric; + if (!strcmp(szRenderingIntent, "Saturation")) + return icSaturation; + if (!strcmp(szRenderingIntent, "ICC-absolute colorimetric") || + !strcmp(szRenderingIntent, "Absolute") || + !strcmp(szRenderingIntent, "Absolute Colorimetric")) + return icAbsoluteColorimetric; + return icPerceptual; +} + +icStandardObserver icGetNamedStandardObserverValue(const icChar *str) +{ + if (!strcmp(str, "Unknown observer")) + return icStdObsUnknown; + if (!strcmp(str, "CIE 1931 (two degree) standard observer")) + return icStdObs1931TwoDegrees; + if (!strcmp(str, "CIE 1964 (ten degree) standard observer")) + return icStdObs1964TenDegrees; + return icStdObsCustom; +} + +icIlluminant icGetIlluminantValue(const icChar *str) +{ + if (!strcmp(str, "Illuminant Unknown")) return icIlluminantUnknown; + if (!strcmp(str, "Illuminant D50") || !strcmp(str, "D50")) return icIlluminantD50; + if (!strcmp(str, "Illuminant D65") || !strcmp(str, "D65")) return icIlluminantD65; + if (!strcmp(str, "Illuminant D93") || !strcmp(str, "D93")) return icIlluminantD93; + if (!strcmp(str, "Illuminant F2") || !strcmp(str, "F2")) return icIlluminantF2; + if (!strcmp(str, "Illuminant D55") || !strcmp(str, "D55")) return icIlluminantD55; + if (!strcmp(str, "Illuminant A") || !strcmp(str, "A")) return icIlluminantA; + if (!strcmp(str, "Illuminant EquiPowerE") || !strcmp(str, "Illuminant E") || !strcmp(str, "E")) return icIlluminantEquiPowerE; + if (!strcmp(str, "Illuminant F8") || !strcmp(str, "F8")) return icIlluminantF8; + if (!strcmp(str, "Illuminant Black Body") || !strcmp(str, "Black Body")) return icIlluminantBlackBody; + if (!strcmp(str, "Illuminant Daylight") || !strcmp(str, "Daylight")) return icIlluminantDaylight; + if (!strcmp(str, "Illuminant B") || !strcmp(str, "B")) return icIlluminantB; + if (!strcmp(str, "Illuminant C") || !strcmp(str, "C")) return icIlluminantC; + if (!strcmp(str, "Illuminant F1") || !strcmp(str, "F1")) return icIlluminantF1; + if (!strcmp(str, "Illuminant F3") || !strcmp(str, "F3")) return icIlluminantF3; + if (!strcmp(str, "Illuminant F4") || !strcmp(str, "F4")) return icIlluminantF4; + if (!strcmp(str, "Illuminant F5") || !strcmp(str, "F5")) return icIlluminantF5; + if (!strcmp(str, "Illuminant F6") || !strcmp(str, "F6")) return icIlluminantF6; + if (!strcmp(str, "Illuminant F7") || !strcmp(str, "F7")) return icIlluminantF7; + if (!strcmp(str, "Illuminant F9") || !strcmp(str, "F9")) return icIlluminantF9; + if (!strcmp(str, "Illuminant F10") || !strcmp(str, "F10")) return icIlluminantF10; + if (!strcmp(str, "Illuminant F11") || !strcmp(str, "F11")) return icIlluminantF11; + if (!strcmp(str, "Illuminant F12") || !strcmp(str, "F12")) return icIlluminantF12; + return icIlluminantCustom; +} + +icMeasurementUnitSig icGetMeasurementValue(const icChar *str) +{ + if (!strcmp(str, "Status A")) return icSigStatusA; + if (!strcmp(str, "Status E")) return icSigStatusE; + if (!strcmp(str, "Status I")) return icSigStatusI; + if (!strcmp(str, "Status T")) return icSigStatusT; + if (!strcmp(str, "Status M")) return icSigStatusM; + if (!strcmp(str, "DIN with no polarizing filter")) return icSigDN; + if (!strcmp(str, "DIN with polarizing filter")) return icSigDNP; + if (!strcmp(str, "Narrow band DIN with no polarizing filter")) return icSigDNN; + if (!strcmp(str, "Narrow band DIN with polarizing filter")) return icSigDNNP; + return icSigStatusA; +} + #ifdef USEICCDEVNAMESPACE } //namespace iccDEV #endif diff --git a/IccProfLib/IccUtil.h b/IccProfLib/IccUtil.h index 90ca1c57c..109ddda41 100644 --- a/IccProfLib/IccUtil.h +++ b/IccProfLib/IccUtil.h @@ -191,7 +191,7 @@ ICCPROFLIB_API icSignature icGetLastSigPathSig(std::string sigPath); ICCPROFLIB_API icUInt32Number icGetSigVal(const icChar *pBuf); ICCPROFLIB_API icUInt32Number icGetSpaceSamples(icColorSpaceSignature sig); ICCPROFLIB_API icUInt32Number icGetSpectralSpaceSamples(const icHeader *pHdr); -ICCPROFLIB_API icUInt32Number icGetMaterialColorSpaceSamples(icMaterialColorSignature sig); +ICCPROFLIB_API icUInt32Number icGetMultiplexColorSpaceSamples(icMultiplexColorSignature sig); bool ICCPROFLIB_API icSameSpectralRange(const icSpectralRange &rng1, const icSpectralRange &rng2); @@ -408,6 +408,57 @@ class ICCPROFLIB_API IIccCmmEnvVarLookup extern ICCPROFLIB_API CIccInfo icInfo; +// --------------------------------------------------------------------------- +// UTF-16 string class and conversion helpers +// (Pure utilities shared between IccXML and IccJSON; no libxml2 dependency) +// --------------------------------------------------------------------------- +class ICCPROFLIB_API CIccUTF16String +{ +public: + CIccUTF16String(); + CIccUTF16String(const icUInt16Number *uzStr); + CIccUTF16String(const char *szStr); + CIccUTF16String(const CIccUTF16String &str); + virtual ~CIccUTF16String(); + + void Clear(); + size_t Size() { return m_len; } + bool Resize(size_t len); + + CIccUTF16String& operator=(const CIccUTF16String &wstr); + CIccUTF16String& operator=(const char *szStr); + CIccUTF16String& operator=(const icUInt16Number *uzStr); + + icUInt16Number operator[](size_t nIndex) { return m_str[nIndex]; } + + const icUInt16Number *c_str() { return m_str; } + + bool FromUtf8(const char *szStr, size_t sizeSrc=0); + const char *ToUtf8(std::string &buf); + const wchar_t *ToWString(std::wstring &buf); + + static size_t WStrlen(const icUInt16Number *uzStr); + +protected: + static size_t AllocSize(size_t n) { return (((n+64)/64)*64); } + size_t m_alloc; + size_t m_len; + icUInt16Number *m_str; +}; + +ICCPROFLIB_API const char *icUtf16ToUtf8(std::string &buf, const icUInt16Number *szSrc, int sizeSrc=0); +ICCPROFLIB_API const unsigned short *icUtf8ToUtf16(CIccUTF16String &buf, const char *szSrc, int sizeSrc=0); + +// --------------------------------------------------------------------------- +// Date/time and rendering intent parsing helpers +// (Pure utilities shared between IccXML and IccJSON; no libxml2 dependency) +// --------------------------------------------------------------------------- +ICCPROFLIB_API icDateTimeNumber icGetDateTimeValue(const icChar *str); +ICCPROFLIB_API icRenderingIntent icGetRenderingIntentValue(const icChar *szRenderingIntent); +ICCPROFLIB_API icStandardObserver icGetNamedStandardObserverValue(const icChar *str); +ICCPROFLIB_API icIlluminant icGetIlluminantValue(const icChar *str); +ICCPROFLIB_API icMeasurementUnitSig icGetMeasurementValue(const icChar *str); + #ifdef USEICCDEVNAMESPACE } //namespace iccDEV #endif diff --git a/IccProfLib/icProfileHeader.h b/IccProfLib/icProfileHeader.h index 2844b0d55..27cdebbe4 100644 --- a/IccProfLib/icProfileHeader.h +++ b/IccProfLib/icProfileHeader.h @@ -957,7 +957,7 @@ typedef enum : icUInt32Number { icSigMCSDataEnd = 0x6d63FFFF, // provide clues to UBSan icSigMCSMaxEnumData = 0xFFFFFFFF, -} icMaterialColorSignature; +} icMultiplexColorSignature; #define icGetColorSpaceType(sig) ((icColorSpaceSignature)(((icUInt32Number)sig)&0xffff0000)) #define icIsSameColorSpaceType(sig, type) ((((icUInt32Number)sig)&0xffff0000)==((icUInt32Number)(type))) @@ -987,9 +987,9 @@ typedef enum { icSigColorSpaceClass = 0x73706163, /* 'spac' */ icSigNamedColorClass = 0x6e6d636c, /* 'nmcl' */ icSigColorEncodingClass = 0x63656e63, /* 'cenc' */ - icSigMaterialIdentificationClass = 0x6D696420, /* 'mid ' */ - icSigMaterialLinkClass = 0x6d6c6e6b, /* 'mlnk' */ - icSigMaterialVisualizationClass = 0x6d766973, /* 'mvis' */ + icSigMultiplexIdentificationClass = 0x6D696420, /* 'mid ' */ + icSigMultiplexLinkClass = 0x6d6c6e6b, /* 'mlnk' */ + icSigMultiplexVisualizationClass = 0x6d766973, /* 'mvis' */ /* Convenience Enum Definition - Not defined in ICC specification */ icMaxEnumClass = 0xFFFFFFFF, @@ -2096,7 +2096,7 @@ typedef struct { icColorSpaceSignature spectralPCS; /* Spectral colour space signature */ icSpectralRange spectralRange; /* Start, end, and steps for spectral PCS */ icSpectralRange biSpectralRange; /* Start, end, and steps for bi-spectral PCS */ - icMaterialColorSignature mcs; /* Material Connection Space */ + icMultiplexColorSignature mcs; /* Multiplex Connection Space */ icSignature deviceSubClass; /* Refinement on type of profile */ icInt8Number reserved[4]; /* Reserved for future use */ diff --git a/IccXML/IccLibXML/IccMpeXml.cpp b/IccXML/IccLibXML/IccMpeXml.cpp index a3d243c87..7ac96bd69 100644 --- a/IccXML/IccLibXML/IccMpeXml.cpp +++ b/IccXML/IccLibXML/IccMpeXml.cpp @@ -206,7 +206,8 @@ bool CIccFormulaCurveSegmentXml::ParseXml(xmlNode *pNode, std::string &parseStr) return false; } - m_nReserved2 = atoi(icXmlAttrValue(pNode, "Reserved2")); + m_nReserved = atoi(icXmlAttrValue(pNode, "Reserved", "0")); + m_nReserved2 = atoi(icXmlAttrValue(pNode, "Reserved2", "0")); m_nFunctionType = atoi(icXmlAttrValue(funcType)); @@ -542,13 +543,18 @@ bool CIccSampledCalculatorCurveXml::ToXml(std::string &xml, std::string blanks) snprintf(line, lineSize, " ExtensionType=\"%u\"", m_extensionType); xml += line; - snprintf(line, lineSize, " DesiredSize=\"%u\">\n", m_nDesiredSize); + snprintf(line, lineSize, " DesiredSize=\"%u\"", m_nDesiredSize); xml += line; + if (m_nReserved) { + snprintf(line, lineSize, " Reserved=\"%u\"", m_nReserved); + xml += line; + } if (m_nReserved2) { - snprintf(line, lineSize, " Reservered2=\"%u\">\n", m_nReserved2); + snprintf(line, lineSize, " Reserved2=\"%u\"", m_nReserved2); xml += line; } + xml += ">\n"; if (m_pCalc && !strcmp(m_pCalc->GetClassName(), "CIccMpeXmlCalculator")) { CIccMpeXmlCalculator *pXmlCalc = (CIccMpeXmlCalculator*)m_pCalc; @@ -594,6 +600,9 @@ bool CIccSampledCalculatorCurveXml::ParseXml(xmlNode *pNode, std::string &parseS m_nDesiredSize = (icUInt32Number)atoi(icXmlAttrValue(attr)); + m_nReserved = (icUInt32Number)atoi(icXmlAttrValue(pNode, "Reserved", "0")); + m_nReserved2 = (icUInt16Number)atoi(icXmlAttrValue(pNode, "Reserved2", "0")); + xmlNode *pCalcNode = icXmlFindNode(pNode->children, "CalculatorElement"); if (pCalcNode) { CIccMpeXmlCalculator *pCalc = new CIccMpeXmlCalculator(); @@ -639,9 +648,15 @@ bool CIccSingleSampledCurveXml::ToXml(std::string &xml, std::string blanks) snprintf(line, lineSize, " StorageType=\"%u\"", m_storageType); xml += line; - snprintf(line, lineSize, " ExtensionType=\"%u\">\n", m_extensionType); + snprintf(line, lineSize, " ExtensionType=\"%u\"", m_extensionType); xml += line; + if (m_nReserved) { + snprintf(line, lineSize, " Reserved=\"%u\"", m_nReserved); + xml += line; + } + xml += ">\n"; + CIccFloatArray::DumpArray(xml, blanks + " ", m_pSamples, m_nCount, icConvertFloat, 8); xml += blanks + "\n"; @@ -682,6 +697,8 @@ bool CIccSingleSampledCurveXml::ParseXml(xmlNode *pNode, std::string &parseStr) m_extensionType = (icUInt16Number)atoi(icXmlAttrValue(attr)); } + m_nReserved = (icUInt32Number)atoi(icXmlAttrValue(pNode, "Reserved", "0")); + const char *filename = icXmlAttrValue(pNode, "Filename"); // file exists diff --git a/IccXML/IccLibXML/IccProfileXml.cpp b/IccXML/IccLibXML/IccProfileXml.cpp index 93ccc8ad8..800d69d50 100644 --- a/IccXML/IccLibXML/IccProfileXml.cpp +++ b/IccXML/IccLibXML/IccProfileXml.cpp @@ -551,7 +551,7 @@ bool CIccProfileXml::ParseBasic(xmlNode *pNode, std::string &parseStr) } } else if (!icXmlStrCmp(pNode->name, "MCS")) { - m_Header.mcs = (icMaterialColorSignature)icXmlGetChildSigVal(pNode); + m_Header.mcs = (icMultiplexColorSignature)icXmlGetChildSigVal(pNode); } else if (!icXmlStrCmp(pNode->name, "ProfileDeviceSubClass")) { m_Header.deviceSubClass = (icProfileClassSignature)icXmlGetChildSigVal(pNode); diff --git a/IccXML/IccLibXML/IccUtilXml.cpp b/IccXML/IccLibXML/IccUtilXml.cpp index 3ef4dd590..8754e3d26 100644 --- a/IccXML/IccLibXML/IccUtilXml.cpp +++ b/IccXML/IccLibXML/IccUtilXml.cpp @@ -79,201 +79,7 @@ -CIccUTF16String::CIccUTF16String() -{ - m_alloc=64; - m_len = 0; - m_str = (icUInt16Number*)calloc(m_alloc, sizeof(icUInt16Number)); -} - -CIccUTF16String::CIccUTF16String(const icUInt16Number *uzStr) -{ - m_len = WStrlen(uzStr); - m_alloc = AllocSize(m_len); - - m_str = (icUInt16Number *)malloc(m_alloc*sizeof(icUInt16Number)); - memcpy(m_str, uzStr, m_len+1*sizeof(icUInt16Number)); -} - -CIccUTF16String::CIccUTF16String(const char *szStr) -{ - size_t sizeSrc = strlen(szStr); - - if (sizeSrc) { - m_alloc = AllocSize(sizeSrc*2); - m_str = (UTF16 *)calloc(m_alloc, sizeof(icUInt16Number)); //overallocate to allow for up to 4 bytes per character - UTF16 *szDest = m_str; - icConvertUTF8toUTF16((const UTF8 **)&szStr, (const UTF8 *)&szStr[sizeSrc], &szDest, &szDest[m_alloc], lenientConversion); - if (m_str[0]==0xfeff) { - size_t i; - for (i=1; m_str[i]; i++) - m_str[i-1] = m_str[i]; - m_str[i-1] = 0; - } - m_len = WStrlen(m_str); - } - else { - m_alloc = 64; - m_len = 0; - m_str = (icUInt16Number*)calloc(m_alloc, sizeof(icUInt16Number)); - } -} - -CIccUTF16String::CIccUTF16String(const CIccUTF16String &str) -{ - m_alloc = str.m_alloc; - m_len = str.m_len; - m_str = (icUInt16Number*)malloc(m_alloc*sizeof(icUInt16Number)); - - memcpy(m_str, str.m_str, (m_alloc)*sizeof(icUInt16Number)); -} - -CIccUTF16String::~CIccUTF16String() -{ - free(m_str); -} - -void CIccUTF16String::Clear() -{ - m_len = 0; - m_str[0] = 0; -} - -bool CIccUTF16String::Resize(size_t len) -{ - if (len>m_alloc) { - size_t nAlloc = AllocSize(len); - - m_str = (icUInt16Number*)icRealloc(m_str, nAlloc*sizeof(icUInt16Number)); - - if (!m_str) { - m_len = 0; - return false; - } - m_alloc = nAlloc; - } - - if (len>m_len) { - memset(&m_str[m_len], 0x0020, (len-m_len)*sizeof(icUInt16Number)); - } - m_len = len; - m_str[m_len] = 0; - return true; -} - -size_t CIccUTF16String::WStrlen(const icUInt16Number *uzStr) -{ - size_t n=0; - while(uzStr[n]) n++; - - return n; -} - -CIccUTF16String& CIccUTF16String::operator=(const CIccUTF16String &wstr) -{ - if (m_alloc<=wstr.m_alloc) { - m_str = (icUInt16Number*)icRealloc(m_str, wstr.m_alloc *sizeof(icUInt16Number)); - if (m_str) - m_alloc = wstr.m_alloc; - else - m_alloc = 0; - } - if (m_str) { - m_len = wstr.m_len; - - memcpy(m_str, wstr.m_str, (m_len+1)*sizeof(icUInt16Number)); - } - else { - m_len = 0; - } - - return *this; -} - -CIccUTF16String& CIccUTF16String::operator=(const char *szStr) -{ - FromUtf8(szStr, 0); - - return *this; -} - -CIccUTF16String& CIccUTF16String::operator=(const icUInt16Number *uzStr) -{ - size_t n = WStrlen(uzStr); - size_t nAlloc = AllocSize(n); - - if (m_alloc<=nAlloc) { - m_str = (icUInt16Number*)icRealloc(m_str, nAlloc*sizeof(icUInt16Number)); - if (m_str) - m_alloc = nAlloc; - else - m_alloc = 0; - } - if (m_str) { - m_len = n; - - memcpy(m_str, uzStr, (m_len+1)*sizeof(icUInt16Number)); - } - else { - m_len = 0; - } - - return *this; -} - - -bool CIccUTF16String::FromUtf8(const char *szStr, size_t sizeSrc) -{ - if (!sizeSrc) - sizeSrc = strlen(szStr); - - if (sizeSrc) { - size_t nAlloc = AllocSize(sizeSrc*2); - if (m_alloc<=nAlloc) { - m_str = (icUInt16Number*)icRealloc(m_str, nAlloc *sizeof(icUInt16Number)); - m_alloc = nAlloc; - } - if (m_str) { - memset(m_str, 0, m_alloc * sizeof(icUInt16Number)); - UTF16 *szDest = m_str; - icConvertUTF8toUTF16((const UTF8 **)&szStr, (const UTF8 *)&szStr[sizeSrc], &szDest, &szDest[m_alloc], lenientConversion); - if (m_str[0]==0xfeff) { - size_t i; - for (i=1; m_str[i]; i++) - m_str[i-1] = m_str[i]; - m_str[i-1] = 0; - } - m_len = WStrlen(m_str); - } - else { - m_len =0; - return false; - } - } - else { - m_len = 0; - m_str[0] = 0; - } - return true; -} - -const char *CIccUTF16String::ToUtf8(std::string &buf) -{ - return icUtf16ToUtf8(buf, m_str, (int)m_len); -} - -const wchar_t *CIccUTF16String::ToWString(std::wstring &buf) -{ - size_t i; - - buf.clear(); - - for (i=0; i; template class CIccXmlArrayType; -icRenderingIntent icGetRenderingIntentValue (const icChar *szRenderingIntent) -{ - if (!strcmp(szRenderingIntent, "Perceptual")) - return icPerceptual; - else if (!strcmp(szRenderingIntent, "Media-relative colorimetric")) - return icRelativeColorimetric; - else if (!strcmp(szRenderingIntent, "Saturation")) - return icSaturation; - else if (!strcmp(szRenderingIntent, "ICC-absolute colorimetric")) - return icAbsoluteColorimetric; - - return icPerceptual; -} +// icGetRenderingIntentValue is now implemented in IccProfLib/IccUtil.cpp const icChar* icGetTagSigTypeName(icTagTypeSignature tagTypeSig) { @@ -1243,22 +1008,7 @@ icTagSignature icGetTagNameSig(const icChar *szName) } -icStandardObserver icGetNamedStandardObserverValue(const icChar* str) -{ - if (!strcmp(str, "Unknown observer") || !strcmp(str, "Unknown Observer")) - return icStdObsUnknown; - - if (!strcmp(str, "CIE 1931 (two degree) standard observer") || - !strcmp(str, "CIE 1931 standard colorimetric observer")) - return icStdObs1931TwoDegrees; - - if (!strcmp(str, "CIE 1964 (ten degree) standard observer") || - !strcmp(str, "CIE 1964 standard colorimetric observer")) - return icStdObs1964TenDegrees; - - return icStdObsCustom; -} - +// icGetNamedStandardObserverValue is now implemented in IccProfLib/IccUtil.cpp icMeasurementGeometry icGeNamedtMeasurementGeometryValue(const icChar* str) { @@ -1291,79 +1041,7 @@ icMeasurementFlare icGetNamedMeasurementFlareValue(const icChar * str) return icFlare0; } -icIlluminant icGetIlluminantValue(const icChar* str) -{ - if (!strcmp(str, "Illuminant Unknown")) - return icIlluminantUnknown; - - if (!strcmp(str, "Illuminant D50") || !strcmp(str, "D50")) - return icIlluminantD50; - - if (!strcmp(str, "Illuminant D65") || !strcmp(str, "D65")) - return icIlluminantD65; - - if (!strcmp(str, "Illuminant D93") || !strcmp(str, "D93")) - return icIlluminantD93; - - if (!strcmp(str, "Illuminant F2") || !strcmp(str, "F2")) - return icIlluminantF2; - - if (!strcmp(str, "Illuminant D55") || !strcmp(str, "D55")) - return icIlluminantD55; - - if (!strcmp(str, "Illuminant A") || !strcmp(str, "A")) - return icIlluminantA; - - if (!strcmp(str, "Illuminant EquiPowerE") || !strcmp(str, "Illuminant E") || !strcmp(str, "E")) - return icIlluminantEquiPowerE; - - if (!strcmp(str, "Illuminant F8") || !strcmp(str, "F8")) - return icIlluminantF8; - - if (!strcmp(str, "Illuminant Black Body") || !strcmp(str, "Black Body")) - return icIlluminantBlackBody; - - if (!strcmp(str, "Illuminant Daylight") || !strcmp(str, "Daylight")) - return icIlluminantDaylight; - - if (!strcmp(str, "Illuminant B") || !strcmp(str, "B")) - return icIlluminantB; - - if (!strcmp(str, "Illuminant C") || !strcmp(str, "C")) - return icIlluminantC; - - if (!strcmp(str, "Illuminant F1") || !strcmp(str, "F1")) - return icIlluminantF1; - - if (!strcmp(str, "Illuminant F3") || !strcmp(str, "F3")) - return icIlluminantF3; - - if (!strcmp(str, "Illuminant F4") || !strcmp(str, "F4")) - return icIlluminantF4; - - if (!strcmp(str, "Illuminant F5") || !strcmp(str, "F5")) - return icIlluminantF5; - - if (!strcmp(str, "Illuminant F6") || !strcmp(str, "F6")) - return icIlluminantF6; - - if (!strcmp(str, "Illuminant F7") || !strcmp(str, "F7")) - return icIlluminantF7; - - if (!strcmp(str, "Illuminant F9") || !strcmp(str, "F9")) - return icIlluminantF9; - - if (!strcmp(str, "Illuminant F10") || !strcmp(str, "F10")) - return icIlluminantF10; - - if (!strcmp(str, "Illuminant F11") || !strcmp(str, "F11")) - return icIlluminantF11; - - if (!strcmp(str, "Illuminant F12") || !strcmp(str, "F12")) - return icIlluminantF12; - - return icIlluminantCustom; -} +// icGetIlluminantValue is now implemented in IccProfLib/IccUtil.cpp const icChar* icGetStandardObserverName(icStandardObserver str) { @@ -1382,37 +1060,7 @@ const icChar* icGetStandardObserverName(icStandardObserver str) } } -icDateTimeNumber icGetDateTimeValue(const icChar* str) -{ - unsigned int day=0, month=0, year=0, hours=0, minutes=0, seconds=0; - icDateTimeNumber dateTime = {}; - - if (!stricmp(str, "now")) { - time_t rawtime; - struct tm * timeinfo; - - time ( &rawtime ); - timeinfo = localtime ( &rawtime ); - year = timeinfo->tm_year+1900; - month = timeinfo->tm_mon+1; - day = timeinfo->tm_mday; - hours = timeinfo->tm_hour; - minutes = timeinfo->tm_min; - seconds = timeinfo->tm_sec; - } - else { - sscanf(str, "%u-%02u-%02uT%02u:%02u:%02u", &year, &month, &day, &hours, &minutes, &seconds); - } - - dateTime.year = year; - dateTime.month = month; - dateTime.day = day; - dateTime.hours = hours; - dateTime.minutes = minutes; - dateTime.seconds = seconds; - - return dateTime; -} +// icGetDateTimeValue is now implemented in IccProfLib/IccUtil.cpp icUInt64Number icGetDeviceAttrValue(xmlNode *pNode) { @@ -1464,37 +1112,7 @@ icColorantEncoding icGetColorantValue(const icChar* str) return icColorantUnknown; } -icMeasurementUnitSig icGetMeasurementValue(const icChar* str) -{ - if (!strcmp(str, "Status A")) - return icSigStatusA; - - if (!strcmp(str, "Status E")) - return icSigStatusE; - - if (!strcmp(str, "Status I")) - return icSigStatusI; - - if (!strcmp(str, "Status T")) - return icSigStatusT; - - if (!strcmp(str, "Status M")) - return icSigStatusM; - - if (!strcmp(str, "DIN with no polarizing filter")) - return icSigDN; - - if (!strcmp(str, "DIN with polarizing filter")) - return icSigDNP; - - if (!strcmp(str, "Narrow band DIN with no polarizing filter")) - return icSigDNN; - - if (!strcmp(str, "Narrow band DIN with polarizing filter")) - return icSigDNNP; - - return icSigStatusA; -} +// icGetMeasurementValue moved to IccProfLib/IccUtil.cpp const std::string icGetDeviceAttrName(icUInt64Number devAttr) { diff --git a/IccXML/IccLibXML/IccUtilXml.h b/IccXML/IccLibXML/IccUtilXml.h index 00d904072..ee00567f4 100644 --- a/IccXML/IccLibXML/IccUtilXml.h +++ b/IccXML/IccLibXML/IccUtilXml.h @@ -67,49 +67,14 @@ #include #include -class CIccUTF16String -{ -public: - CIccUTF16String(); - CIccUTF16String(const icUInt16Number *uzStr); - CIccUTF16String(const char *szStr); - CIccUTF16String(const CIccUTF16String &str); - virtual ~CIccUTF16String(); - - void Clear(); - size_t Size() { return m_len; } - bool Resize(size_t len); - - CIccUTF16String& operator=(const CIccUTF16String &wstr); - CIccUTF16String& operator=(const char *szStr); - CIccUTF16String& operator=(const icUInt16Number *uzStr); - - icUInt16Number operator[](size_t m_nIndex) { return m_str[m_nIndex]; } - - const icUInt16Number *c_str() { return m_str; } - - bool FromUtf8(const char *szStr, size_t sizeSrc=0); - const char *ToUtf8(std::string &buf); - const wchar_t *ToWString(std::wstring &buf); - - static size_t WStrlen(const icUInt16Number *uzStr); - -protected: - static size_t AllocSize(size_t n) { return (((n+64)/64)*64); } - size_t m_alloc; - size_t m_len; - icUInt16Number *m_str; -}; - +// CIccUTF16String, icUtf16ToUtf8, icUtf8ToUtf16 are now declared in IccUtil.h +// and available here via the #include "IccUtil.h" above. const char *icFixXml(char *szDest, const char *szStr); const char *icFixXml(std::string &buf, const char *szStr); const char *icAnsiToUtf8(std::string &buf, const char *szSrc); const char *icUtf8ToAnsi(std::string &buf, const char *szSrc); -const char *icUtf16ToUtf8(std::string &buf, const icUInt16Number *szSrc, int sizeSrc=0); -const unsigned short *icUtf8ToUtf16(CIccUTF16String &buf, const char *szSrc, int sizeSrc=0); - bool icCLUTDataToXml(std::string &xml, CIccCLUT *pCLUT, icConvertType nType, std::string blanks, bool bSaveGridPoints=false); bool icCLUTToXml(std::string &xml, CIccCLUT *pCLUT, icConvertType nType, std::string blanks, @@ -178,16 +143,14 @@ const icChar* icGetTagSigTypeName(icTagTypeSignature tagTypeSig); icTagTypeSignature icGetTypeNameTagSig(const icChar *szTagType); const icChar* icGetTagSigName(icTagSignature tagSig); icTagSignature icGetTagNameSig(const icChar *szTagName); -icRenderingIntent icGetRenderingIntentValue (const icChar *szRenderingIntent); -icStandardObserver icGetNamedStandardObserverValue(const icChar *str); +// icGetRenderingIntentValue, icGetDateTimeValue, icGetNamedStandardObserverValue, +// and icGetIlluminantValue are now in IccUtil.h icMeasurementGeometry icGeNamedtMeasurementGeometryValue(const icChar *str); icMeasurementFlare icGetNamedMeasurementFlareValue(const icChar *str); -icIlluminant icGetIlluminantValue(const icChar *str); const icChar* icGetStandardObserverName(icStandardObserver str); -icDateTimeNumber icGetDateTimeValue(const icChar *str); icUInt64Number icGetDeviceAttrValue(xmlNode *pNode); icColorantEncoding icGetColorantValue(const icChar* str); -icMeasurementUnitSig icGetMeasurementValue(const icChar* str); +// icGetMeasurementValue moved to IccProfLib/IccUtil.h const std::string icGetDeviceAttrName(icUInt64Number devAttr); const std::string icGetHeaderFlagsName(icUInt32Number flags, bool bUsesMCS=false); const std::string icGetPadSpace(double value); diff --git a/Testing/hybrid/MS-Mid_Overprint.xml b/Testing/hybrid/MS-Mid_Overprint.xml index 170529075..b25d63afb 100644 --- a/Testing/hybrid/MS-Mid_Overprint.xml +++ b/Testing/hybrid/MS-Mid_Overprint.xml @@ -39,12 +39,12 @@ - + ProcessMagenta Silver - + diff --git a/Testing/hybrid/SC-Mid_Overprint.xml b/Testing/hybrid/SC-Mid_Overprint.xml index 07cd1d86f..03928eaca 100644 --- a/Testing/hybrid/SC-Mid_Overprint.xml +++ b/Testing/hybrid/SC-Mid_Overprint.xml @@ -39,12 +39,12 @@ - + Silver ProcessCyan - + diff --git a/Testing/sRGB_v4_ICC_preference.json b/Testing/sRGB_v4_ICC_preference.json new file mode 100644 index 000000000..fd1fd908e --- /dev/null +++ b/Testing/sRGB_v4_ICC_preference.json @@ -0,0 +1,30139 @@ +{ + "IccProfile": { + "Header": { + "CMMFlags": { + "EmbeddedInFile": false, + "UseWithEmbeddedDataOnly": false + }, + "CreationDateTime": "2007-07-25T00:05:37", + "DataColourSpace": "RGB ", + "DeviceAttributes": { + "GlossyOrMatte": "glossy", + "MediaColour": "colour", + "MediaPolarity": "positive", + "ReflectiveOrTransparency": "reflective" + }, + "DeviceManufacturer": "", + "DeviceModel": "", + "PCS": "Lab ", + "PCSIlluminant": [ + 0.964202880859375, + 1.0, + 0.8249053955078125 + ], + "PreferredCMMType": "", + "PrimaryPlatform": "", + "ProfileCreator": "", + "ProfileDeviceClass": "spac", + "ProfileFileSignature": "acsp", + "ProfileID": "34562abf994ccd066d2c5721d0d68c5d", + "ProfileVersion": "4.20", + "RenderingIntent": "Perceptual" + }, + "Tags": [ + { + "profileDescriptionTag": { + "data": { + "localizedStrings": [ + { + "country": "US", + "language": "en", + "text": "sRGB v4 ICC preference perceptual intent beta" + } + ], + "type": "multiLocalizedUnicodeType" + } + } + }, + { + "AToB0Tag": { + "data": { + "aCurves": [ + { + "curveType": "identity", + "type": "Curve" + }, + { + "curveType": "identity", + "type": "Curve" + }, + { + "curveType": "identity", + "type": "Curve" + } + ], + "bCurves": [ + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + } + ], + "clut": { + "data": [ + 2039, + 32896, + 32896, + 1977, + 33931, + 30585, + 2114, + 34898, + 28323, + 2401, + 35914, + 26063, + 3194, + 36948, + 24026, + 3739, + 37999, + 22134, + 4432, + 39055, + 20242, + 5432, + 40018, + 18650, + 6401, + 40930, + 17034, + 7113, + 41643, + 15853, + 7748, + 42231, + 14855, + 8426, + 42741, + 13902, + 9145, + 43208, + 12942, + 9791, + 43669, + 11958, + 10387, + 44170, + 10752, + 11290, + 44624, + 9652, + 12240, + 45059, + 8110, + 2457, + 30726, + 34392, + 3632, + 31383, + 31847, + 4073, + 32476, + 29475, + 4544, + 33592, + 27237, + 5111, + 34647, + 25008, + 5803, + 35882, + 22928, + 6476, + 37043, + 20883, + 7197, + 38108, + 19142, + 8033, + 39090, + 17501, + 8944, + 39974, + 16101, + 9737, + 40688, + 15090, + 10387, + 41282, + 14160, + 11002, + 41793, + 13250, + 11668, + 42258, + 12366, + 12410, + 42722, + 11328, + 13180, + 43159, + 10323, + 13894, + 43537, + 9401, + 5280, + 28669, + 35710, + 5650, + 29265, + 33336, + 6030, + 30089, + 30904, + 6465, + 31117, + 28504, + 7003, + 32324, + 26115, + 7569, + 33576, + 23854, + 8194, + 34897, + 21728, + 8942, + 36089, + 19817, + 9653, + 37115, + 18173, + 10455, + 38110, + 16606, + 11245, + 38914, + 15484, + 12011, + 39663, + 14498, + 12589, + 40244, + 13504, + 13441, + 40988, + 12653, + 14140, + 41472, + 11740, + 14872, + 41898, + 10848, + 15594, + 42297, + 9976, + 7828, + 26485, + 37250, + 7844, + 27016, + 34849, + 7907, + 27621, + 32435, + 8245, + 28678, + 29869, + 8671, + 29725, + 27426, + 9318, + 30994, + 25052, + 10060, + 32517, + 22806, + 10626, + 33812, + 20665, + 11335, + 34965, + 18935, + 12009, + 36022, + 17363, + 12746, + 37001, + 16022, + 13480, + 37827, + 14893, + 13982, + 38466, + 13789, + 14881, + 39349, + 12898, + 15784, + 40192, + 12054, + 16552, + 40757, + 11198, + 17248, + 41139, + 10353, + 10722, + 24402, + 38842, + 10792, + 24720, + 36607, + 10076, + 24918, + 34041, + 10255, + 25904, + 31606, + 10381, + 26932, + 28925, + 11192, + 28339, + 26494, + 11779, + 29694, + 24108, + 12474, + 31277, + 21859, + 13118, + 32695, + 19802, + 13773, + 33815, + 18150, + 14231, + 34798, + 16465, + 14738, + 35693, + 15256, + 15375, + 36565, + 14148, + 16494, + 37653, + 13330, + 17282, + 38501, + 12407, + 18004, + 39173, + 11539, + 18710, + 39752, + 10674, + 13905, + 22173, + 40811, + 13617, + 22422, + 38567, + 13281, + 22717, + 36282, + 12709, + 23277, + 33681, + 12481, + 24106, + 30888, + 12914, + 25362, + 28152, + 13623, + 26815, + 25623, + 14407, + 28412, + 23308, + 14932, + 29908, + 21033, + 15577, + 31318, + 19194, + 15990, + 32466, + 17523, + 16867, + 33776, + 16160, + 17412, + 34815, + 14922, + 18288, + 35892, + 13913, + 18940, + 36800, + 12880, + 19573, + 37585, + 11926, + 20252, + 38275, + 11022, + 16807, + 20014, + 42728, + 16771, + 20238, + 40620, + 16315, + 20404, + 38387, + 15730, + 20642, + 35967, + 14836, + 21088, + 33181, + 14995, + 22287, + 30255, + 15539, + 23822, + 27322, + 16173, + 25396, + 24767, + 16665, + 26974, + 22287, + 17373, + 28614, + 20260, + 17974, + 30047, + 18482, + 18689, + 31443, + 16919, + 19429, + 32801, + 15675, + 20068, + 33926, + 14507, + 20695, + 34934, + 13418, + 21286, + 35830, + 12365, + 21994, + 36659, + 11379, + 19958, + 17937, + 44956, + 19709, + 18013, + 42802, + 19461, + 18120, + 40656, + 18852, + 18253, + 38326, + 17804, + 18184, + 35796, + 17688, + 19248, + 32902, + 17584, + 20558, + 29556, + 18099, + 22263, + 26518, + 18639, + 23997, + 23791, + 19289, + 25683, + 21515, + 19924, + 27320, + 19585, + 20555, + 28917, + 17916, + 21291, + 30358, + 16344, + 22044, + 31699, + 15198, + 22642, + 32887, + 14055, + 23174, + 33848, + 12940, + 23767, + 34753, + 11862, + 23515, + 16040, + 46761, + 23615, + 16327, + 44741, + 22973, + 16212, + 42753, + 22261, + 16041, + 40721, + 21093, + 15734, + 38271, + 20626, + 16380, + 35421, + 20204, + 17218, + 32254, + 20258, + 18655, + 28902, + 20562, + 20760, + 25531, + 21290, + 22663, + 23010, + 21943, + 24394, + 20791, + 22611, + 26075, + 19019, + 23238, + 27682, + 17344, + 24058, + 29241, + 15954, + 24655, + 30533, + 14749, + 25188, + 31735, + 13575, + 25679, + 32848, + 12399, + 26921, + 14674, + 48151, + 26761, + 14621, + 46383, + 26536, + 14540, + 44616, + 25865, + 14146, + 42792, + 24942, + 13583, + 40904, + 24002, + 13622, + 38200, + 23412, + 14066, + 35280, + 23076, + 15009, + 31867, + 23064, + 17096, + 28180, + 23560, + 19582, + 24762, + 24106, + 21477, + 22347, + 24836, + 23194, + 20320, + 25253, + 24834, + 18510, + 26034, + 26500, + 16970, + 26739, + 28079, + 15575, + 27276, + 29414, + 14290, + 27740, + 30604, + 13023, + 30360, + 13767, + 49248, + 30134, + 13576, + 47506, + 29857, + 13347, + 45953, + 29321, + 12898, + 44353, + 28434, + 11947, + 42747, + 28062, + 11478, + 41095, + 26736, + 10173, + 38647, + 26166, + 11854, + 34992, + 25759, + 13114, + 31289, + 26094, + 15694, + 27507, + 26429, + 18121, + 24234, + 27006, + 20214, + 21788, + 27355, + 21852, + 19747, + 27969, + 23637, + 18133, + 28632, + 25306, + 16583, + 29347, + 26926, + 15203, + 29896, + 28357, + 13794, + 33357, + 13120, + 49737, + 33297, + 12973, + 48248, + 33133, + 12649, + 46900, + 32874, + 12202, + 45592, + 32214, + 11572, + 44076, + 31439, + 10973, + 42346, + 30681, + 10245, + 40575, + 29739, + 9588, + 37948, + 29093, + 9994, + 34686, + 28891, + 12087, + 30720, + 28971, + 14356, + 26805, + 29276, + 16918, + 23686, + 29665, + 18939, + 21204, + 30244, + 20922, + 19405, + 30827, + 22723, + 17843, + 31436, + 24424, + 16334, + 31934, + 25948, + 14795, + 35935, + 12714, + 50081, + 35919, + 12649, + 48648, + 35782, + 12297, + 47414, + 35230, + 11409, + 46267, + 35173, + 11252, + 45024, + 34426, + 10608, + 43419, + 33448, + 9615, + 41825, + 32909, + 9443, + 39746, + 32075, + 9030, + 37141, + 31615, + 9499, + 33833, + 30970, + 9916, + 29911, + 31245, + 12908, + 25964, + 31090, + 15298, + 22642, + 31933, + 17863, + 20609, + 32480, + 19784, + 18859, + 33479, + 21862, + 17509, + 34014, + 23560, + 15851, + 38141, + 12353, + 50372, + 38184, + 12357, + 48969, + 38123, + 12110, + 47802, + 38019, + 11803, + 46659, + 37867, + 11437, + 45515, + 37520, + 11041, + 44161, + 36964, + 10527, + 42639, + 36489, + 10215, + 41075, + 35626, + 9675, + 38807, + 34619, + 8969, + 36261, + 34526, + 9825, + 33046, + 34225, + 10364, + 29349, + 33987, + 12797, + 25573, + 34594, + 15670, + 22841, + 35147, + 17871, + 20770, + 35715, + 19671, + 19007, + 36181, + 21304, + 17282, + 40172, + 11919, + 50711, + 40293, + 12019, + 49294, + 40284, + 11875, + 48141, + 40223, + 11669, + 47023, + 40143, + 11434, + 45903, + 39977, + 11159, + 44717, + 39530, + 10786, + 43247, + 39068, + 10389, + 41781, + 38574, + 10045, + 40082, + 38078, + 9858, + 37901, + 37664, + 9944, + 35322, + 37449, + 10477, + 32271, + 37163, + 11201, + 28797, + 37222, + 13607, + 25579, + 37580, + 15884, + 22887, + 37793, + 17670, + 20682, + 38280, + 19351, + 18727, + 42154, + 11333, + 51068, + 42300, + 11486, + 49641, + 42314, + 11345, + 48468, + 42273, + 11142, + 47356, + 42212, + 10900, + 46258, + 42140, + 10656, + 45154, + 41895, + 10453, + 43801, + 41579, + 10228, + 42415, + 41183, + 9980, + 41017, + 40762, + 9935, + 39073, + 40326, + 9774, + 37057, + 40054, + 10169, + 34422, + 39910, + 10903, + 31533, + 39799, + 12125, + 28429, + 39861, + 14003, + 25370, + 40067, + 15955, + 22749, + 40271, + 17512, + 20516, + 44371, + 10670, + 51387, + 44458, + 10775, + 49982, + 44428, + 10634, + 48779, + 44421, + 10303, + 47706, + 44393, + 10016, + 46594, + 44393, + 9654, + 45519, + 44351, + 9405, + 44289, + 44334, + 9126, + 42961, + 44298, + 8842, + 41658, + 44125, + 8736, + 40108, + 43745, + 8839, + 38171, + 43166, + 9058, + 36116, + 42600, + 9702, + 33572, + 42292, + 10814, + 30903, + 42288, + 12111, + 28241, + 42283, + 13841, + 25292, + 42293, + 15739, + 22523, + 2155, + 34715, + 34097, + 2998, + 35562, + 31571, + 3447, + 36330, + 29379, + 4041, + 37202, + 27209, + 4558, + 38074, + 25065, + 5152, + 39086, + 23103, + 5856, + 40056, + 21100, + 6697, + 40999, + 19372, + 7422, + 41812, + 17876, + 8110, + 42508, + 16464, + 8753, + 43074, + 15438, + 9466, + 43541, + 14447, + 10184, + 43978, + 13484, + 10952, + 44364, + 12561, + 11730, + 44801, + 11461, + 12528, + 45223, + 10277, + 13247, + 45550, + 9384, + 4811, + 32495, + 35268, + 5645, + 32896, + 32896, + 5831, + 33941, + 30575, + 6179, + 34883, + 28280, + 6725, + 35922, + 25997, + 7339, + 37008, + 23806, + 7812, + 38060, + 21764, + 8475, + 39058, + 19870, + 9330, + 40009, + 18255, + 10242, + 40929, + 16586, + 10831, + 41541, + 15583, + 11441, + 42077, + 14654, + 12086, + 42558, + 13753, + 12788, + 42995, + 12871, + 13536, + 43407, + 11942, + 14283, + 43789, + 10986, + 15013, + 44126, + 10101, + 7001, + 30481, + 36500, + 7456, + 30945, + 34212, + 7922, + 31542, + 31927, + 8391, + 32522, + 29535, + 8787, + 33614, + 27167, + 9232, + 34699, + 24817, + 9803, + 35922, + 22677, + 10382, + 37015, + 20582, + 11056, + 38030, + 18972, + 11810, + 39016, + 17304, + 12592, + 39870, + 15937, + 13324, + 40583, + 14947, + 13977, + 41202, + 14029, + 14618, + 41722, + 13145, + 15302, + 42191, + 12264, + 16020, + 42568, + 11409, + 16705, + 42901, + 10569, + 9581, + 28430, + 37888, + 9923, + 28863, + 35614, + 10180, + 29358, + 33371, + 10450, + 30144, + 30972, + 10797, + 31082, + 28548, + 11336, + 32340, + 26123, + 11792, + 33586, + 23788, + 12247, + 34793, + 21618, + 12893, + 35896, + 19766, + 13627, + 36985, + 18204, + 14305, + 37992, + 16524, + 14987, + 38812, + 15416, + 15667, + 39592, + 14406, + 16368, + 40377, + 13448, + 17062, + 41014, + 12544, + 17711, + 41388, + 11706, + 18365, + 41739, + 10861, + 12214, + 26351, + 39482, + 12561, + 26608, + 37289, + 12660, + 27102, + 34995, + 12864, + 27666, + 32698, + 13171, + 28682, + 30041, + 13413, + 29709, + 27604, + 13851, + 30931, + 25202, + 14424, + 32344, + 22930, + 14900, + 33587, + 20786, + 15492, + 34744, + 19098, + 16182, + 35922, + 17418, + 16878, + 36963, + 15993, + 17441, + 37769, + 14948, + 18039, + 38539, + 13946, + 18663, + 39270, + 12974, + 19313, + 39924, + 12048, + 20007, + 40466, + 11192, + 15538, + 24160, + 41228, + 15766, + 24475, + 39112, + 15988, + 24787, + 37059, + 15693, + 25272, + 34606, + 15562, + 25921, + 32082, + 15719, + 27019, + 29285, + 16181, + 28305, + 26792, + 16522, + 29541, + 24394, + 16985, + 30979, + 22170, + 17594, + 32316, + 20162, + 18118, + 33564, + 18549, + 18665, + 34734, + 16862, + 19267, + 35783, + 15607, + 19863, + 36736, + 14526, + 20396, + 37555, + 13486, + 20971, + 38270, + 12475, + 21628, + 38877, + 11565, + 18339, + 22003, + 43067, + 18726, + 22285, + 40945, + 18640, + 22540, + 38836, + 18645, + 22825, + 36727, + 18399, + 23436, + 34115, + 18448, + 24393, + 31333, + 18489, + 25578, + 28471, + 18752, + 26880, + 25984, + 19276, + 28308, + 23724, + 19662, + 29719, + 21516, + 20160, + 31071, + 19586, + 20745, + 32446, + 17943, + 21231, + 33643, + 16334, + 21757, + 34667, + 15172, + 22304, + 35603, + 14066, + 22865, + 36463, + 13001, + 23439, + 37223, + 11970, + 21481, + 19919, + 44955, + 21760, + 20213, + 42792, + 22087, + 20555, + 40665, + 21739, + 20798, + 38529, + 21448, + 21086, + 36327, + 21105, + 21708, + 33601, + 21042, + 22781, + 30619, + 21320, + 24290, + 27652, + 21405, + 25570, + 25104, + 21840, + 27016, + 22934, + 22387, + 28527, + 20912, + 22824, + 29899, + 19072, + 23326, + 31237, + 17363, + 23893, + 32523, + 15926, + 24353, + 33568, + 14749, + 24817, + 34493, + 13602, + 25313, + 35364, + 12447, + 24745, + 17947, + 46795, + 25081, + 18276, + 44714, + 24985, + 18415, + 42713, + 24951, + 18615, + 40660, + 24648, + 18960, + 38338, + 24446, + 19452, + 35906, + 24389, + 20283, + 33160, + 23893, + 21414, + 29939, + 23879, + 22895, + 26888, + 24300, + 24535, + 24308, + 24578, + 25826, + 22209, + 25002, + 27218, + 20253, + 25547, + 28754, + 18574, + 25960, + 30051, + 16829, + 26441, + 31296, + 15477, + 26910, + 32468, + 14250, + 27333, + 33456, + 13010, + 28146, + 16541, + 48148, + 28425, + 16757, + 46273, + 28564, + 16901, + 44425, + 28408, + 16927, + 42595, + 28215, + 16976, + 40706, + 27650, + 17207, + 38282, + 27140, + 17598, + 35645, + 26753, + 18270, + 32687, + 26674, + 19986, + 29310, + 26616, + 21559, + 26176, + 26874, + 23062, + 23676, + 27330, + 24582, + 21619, + 27710, + 25995, + 19771, + 28178, + 27507, + 18090, + 28678, + 28925, + 16384, + 29059, + 30150, + 15040, + 29460, + 31318, + 13690, + 31500, + 15431, + 49190, + 31592, + 15496, + 47475, + 31716, + 15592, + 45813, + 31655, + 15627, + 44087, + 31390, + 15545, + 42340, + 31051, + 15480, + 40472, + 30493, + 15626, + 38074, + 30049, + 16118, + 35299, + 29585, + 16932, + 32069, + 29327, + 18463, + 28626, + 29563, + 20327, + 25515, + 29644, + 21775, + 23093, + 29928, + 23246, + 21026, + 30400, + 24879, + 19330, + 30776, + 26351, + 17672, + 31210, + 27742, + 16046, + 31653, + 29071, + 14555, + 34347, + 14625, + 49828, + 34422, + 14648, + 48240, + 34419, + 14566, + 46782, + 34396, + 14453, + 45344, + 34118, + 14301, + 43708, + 33763, + 14115, + 42013, + 33371, + 13999, + 40079, + 32946, + 14097, + 37687, + 32626, + 14636, + 34802, + 32431, + 15719, + 31527, + 32099, + 17204, + 27974, + 32203, + 19048, + 24736, + 32378, + 20727, + 22494, + 32472, + 22103, + 20444, + 32984, + 23842, + 18883, + 33400, + 25361, + 17273, + 33800, + 26762, + 15604, + 36889, + 14065, + 50260, + 36999, + 14109, + 48719, + 36997, + 13967, + 47402, + 36976, + 13790, + 46110, + 36886, + 13580, + 44795, + 36485, + 13397, + 43161, + 36041, + 13187, + 41511, + 35587, + 13126, + 39451, + 35137, + 13178, + 37132, + 34924, + 13791, + 34075, + 34769, + 14826, + 30749, + 34709, + 16490, + 27346, + 34708, + 18209, + 24247, + 34971, + 19998, + 22091, + 35151, + 21445, + 20194, + 35561, + 23010, + 18524, + 35969, + 24500, + 16788, + 38994, + 13588, + 50602, + 39145, + 13676, + 49070, + 39151, + 13563, + 47831, + 39146, + 13443, + 46601, + 39127, + 13293, + 45395, + 38889, + 13125, + 43952, + 38531, + 12922, + 42398, + 38136, + 12694, + 40805, + 37754, + 12674, + 38676, + 37387, + 12731, + 36396, + 37316, + 13399, + 33319, + 37217, + 14448, + 30047, + 37243, + 16099, + 26855, + 37282, + 17774, + 23966, + 37501, + 19479, + 21829, + 37755, + 20932, + 19960, + 38103, + 22354, + 18134, + 41075, + 13155, + 50916, + 41224, + 13253, + 49407, + 41257, + 13190, + 48189, + 41265, + 13107, + 47013, + 41265, + 13011, + 45850, + 41189, + 12913, + 44605, + 40918, + 12791, + 43124, + 40566, + 12598, + 41624, + 40203, + 12482, + 39862, + 39855, + 12471, + 37820, + 39610, + 12722, + 35407, + 39529, + 13333, + 32578, + 39530, + 14363, + 29467, + 39597, + 15996, + 26415, + 39651, + 17611, + 23685, + 39853, + 19097, + 21568, + 40164, + 20490, + 19644, + 43011, + 12710, + 51143, + 43142, + 12808, + 49705, + 43176, + 12772, + 48460, + 43154, + 12658, + 47311, + 43121, + 12534, + 46172, + 43078, + 12399, + 45048, + 42881, + 12289, + 43677, + 42672, + 12130, + 42294, + 42442, + 11961, + 40890, + 42188, + 12015, + 38962, + 41912, + 12080, + 37016, + 41755, + 12558, + 34561, + 41681, + 13190, + 31897, + 41726, + 14177, + 28978, + 41764, + 15768, + 26052, + 41848, + 17320, + 23408, + 42052, + 18708, + 21208, + 44977, + 12271, + 51381, + 45116, + 12406, + 49942, + 45162, + 12389, + 48694, + 45109, + 12228, + 47563, + 45056, + 11990, + 46464, + 45016, + 11735, + 45369, + 44951, + 11545, + 44142, + 44888, + 11364, + 42859, + 44835, + 11158, + 41567, + 44690, + 11098, + 40017, + 44470, + 11197, + 38126, + 44141, + 11428, + 36164, + 43805, + 12110, + 33841, + 43708, + 12824, + 31331, + 43763, + 13825, + 28584, + 43779, + 15363, + 25766, + 43918, + 16920, + 23093, + 3821, + 36327, + 34916, + 4471, + 36922, + 32673, + 4965, + 37666, + 30482, + 5426, + 38395, + 28352, + 5899, + 39253, + 26148, + 6382, + 40119, + 23970, + 7068, + 41069, + 21938, + 7695, + 41939, + 20152, + 8322, + 42760, + 18721, + 9026, + 43414, + 17184, + 9723, + 43947, + 15932, + 10472, + 44375, + 14930, + 11217, + 44788, + 13971, + 12027, + 45123, + 13074, + 12748, + 45440, + 12146, + 13454, + 45796, + 11096, + 14151, + 46103, + 10143, + 6189, + 34249, + 36041, + 6841, + 34698, + 33813, + 7313, + 35391, + 31551, + 7722, + 36210, + 29251, + 8231, + 37156, + 26988, + 8636, + 38030, + 24740, + 9121, + 39035, + 22675, + 9722, + 39983, + 20581, + 10574, + 40933, + 18914, + 11228, + 41747, + 17337, + 11845, + 42397, + 16029, + 12486, + 42892, + 15109, + 13182, + 43331, + 14223, + 13916, + 43720, + 13356, + 14675, + 44061, + 12492, + 15420, + 44389, + 11600, + 16145, + 44690, + 10753, + 8351, + 32354, + 37219, + 8988, + 32670, + 35016, + 9680, + 32896, + 32896, + 9985, + 33917, + 30525, + 10331, + 34850, + 28176, + 10784, + 35915, + 25802, + 11299, + 36974, + 23565, + 11751, + 37956, + 21480, + 12355, + 38934, + 19640, + 13111, + 39902, + 17969, + 13884, + 40794, + 16314, + 14477, + 41403, + 15395, + 15099, + 41940, + 14499, + 15767, + 42420, + 13627, + 16465, + 42838, + 12764, + 17134, + 43185, + 11908, + 17785, + 43497, + 11060, + 10809, + 30339, + 38622, + 11365, + 30628, + 36394, + 11841, + 31006, + 34216, + 12309, + 31523, + 31967, + 12658, + 32462, + 29539, + 13010, + 33560, + 27137, + 13404, + 34623, + 24749, + 13861, + 35765, + 22600, + 14405, + 36801, + 20509, + 15003, + 37857, + 18875, + 15638, + 38851, + 17122, + 16274, + 39693, + 15830, + 16949, + 40434, + 14864, + 17615, + 41077, + 13951, + 18227, + 41556, + 13077, + 18836, + 41977, + 12195, + 19471, + 42326, + 11351, + 13497, + 28238, + 40105, + 14071, + 28472, + 37912, + 14511, + 28864, + 35703, + 14866, + 29350, + 33503, + 15095, + 30088, + 31094, + 15330, + 30987, + 28619, + 15734, + 32177, + 26216, + 16159, + 33351, + 23911, + 16545, + 34513, + 21777, + 17038, + 35617, + 19863, + 17678, + 36756, + 18211, + 18236, + 37727, + 16556, + 18790, + 38534, + 15473, + 19376, + 39285, + 14472, + 19981, + 39981, + 13507, + 20597, + 40622, + 12556, + 21255, + 41098, + 11675, + 16638, + 26070, + 41741, + 17038, + 26330, + 39634, + 17393, + 26609, + 37546, + 17556, + 27071, + 35249, + 17767, + 27636, + 32907, + 17987, + 28632, + 30275, + 18143, + 29614, + 27813, + 18446, + 30750, + 25459, + 18869, + 31990, + 23223, + 19327, + 33211, + 21102, + 19718, + 34396, + 19321, + 20212, + 35537, + 17669, + 20784, + 36566, + 16178, + 21305, + 37432, + 15093, + 21834, + 38188, + 14050, + 22383, + 38869, + 13043, + 22978, + 39492, + 12056, + 19580, + 23880, + 43422, + 20317, + 24200, + 41260, + 20588, + 24502, + 39205, + 20777, + 24806, + 37173, + 20671, + 25373, + 34731, + 20654, + 26080, + 32208, + 20721, + 27106, + 29508, + 20965, + 28265, + 27080, + 21259, + 29415, + 24798, + 21556, + 30706, + 22585, + 22006, + 31924, + 20483, + 22459, + 33227, + 18812, + 22878, + 34377, + 17119, + 23362, + 35383, + 15787, + 23870, + 36285, + 14679, + 24374, + 37092, + 13592, + 24871, + 37813, + 12480, + 22584, + 21745, + 45187, + 23045, + 22061, + 43093, + 23598, + 22381, + 41042, + 23569, + 22728, + 38900, + 23597, + 23097, + 36754, + 23506, + 23811, + 34100, + 23490, + 24764, + 31333, + 23465, + 25762, + 28673, + 23629, + 26959, + 26283, + 23955, + 28137, + 24091, + 24299, + 29489, + 21996, + 24686, + 30740, + 20002, + 25127, + 32039, + 18296, + 25564, + 33245, + 16610, + 25960, + 34239, + 15365, + 26398, + 35151, + 14179, + 26871, + 36006, + 12982, + 25835, + 19760, + 46833, + 26459, + 20155, + 44618, + 26801, + 20482, + 42622, + 27076, + 20807, + 40669, + 26792, + 21156, + 38507, + 26585, + 21570, + 36258, + 26366, + 22223, + 33613, + 26168, + 23278, + 30683, + 26213, + 24556, + 27844, + 26346, + 25712, + 25480, + 26615, + 26927, + 23356, + 27004, + 28267, + 21375, + 27374, + 29572, + 19515, + 27752, + 30833, + 17789, + 28161, + 32043, + 16172, + 28554, + 33135, + 14896, + 28941, + 34079, + 13604, + 29017, + 18198, + 48352, + 29492, + 18492, + 46298, + 29799, + 18728, + 44331, + 29887, + 18915, + 42463, + 29944, + 19150, + 40520, + 29699, + 19515, + 38258, + 29489, + 19982, + 35865, + 29268, + 20591, + 33209, + 28946, + 21841, + 30108, + 28899, + 23140, + 27228, + 29065, + 24399, + 24736, + 29336, + 25702, + 22722, + 29661, + 26984, + 20760, + 30053, + 28415, + 19046, + 30392, + 29662, + 17337, + 30742, + 30840, + 15762, + 31123, + 31993, + 14333, + 32524, + 16927, + 49347, + 32726, + 17140, + 47568, + 32930, + 17362, + 45781, + 32969, + 17501, + 43983, + 32900, + 17586, + 42204, + 32763, + 17725, + 40281, + 32401, + 17970, + 38023, + 32086, + 18397, + 35503, + 31825, + 18973, + 32760, + 31730, + 20499, + 29525, + 31688, + 21875, + 26571, + 31802, + 23134, + 24119, + 32020, + 24519, + 22105, + 32286, + 25817, + 20222, + 32642, + 27236, + 18598, + 33019, + 28533, + 16929, + 33349, + 29762, + 15297, + 35343, + 15974, + 49986, + 35582, + 16115, + 48296, + 35729, + 16226, + 46744, + 35914, + 16391, + 45157, + 35736, + 16399, + 43472, + 35538, + 16400, + 41778, + 35253, + 16491, + 39837, + 34883, + 16675, + 37643, + 34640, + 17158, + 34984, + 34437, + 17891, + 32061, + 34299, + 19233, + 28851, + 34437, + 20827, + 25753, + 34471, + 22143, + 23506, + 34602, + 23463, + 21584, + 34914, + 24837, + 19790, + 35248, + 26228, + 18114, + 35585, + 27540, + 16359, + 37701, + 15283, + 50515, + 37943, + 15413, + 48819, + 38018, + 15413, + 47426, + 38096, + 15422, + 46027, + 38089, + 15414, + 44572, + 37872, + 15368, + 42943, + 37644, + 15310, + 41311, + 37374, + 15398, + 39302, + 37084, + 15542, + 37170, + 37081, + 16168, + 34334, + 36985, + 17075, + 31291, + 36846, + 18373, + 28179, + 36981, + 19911, + 24912, + 37073, + 21369, + 22986, + 37191, + 22658, + 21185, + 37476, + 23988, + 19369, + 37824, + 25350, + 17524, + 39794, + 14722, + 50891, + 40089, + 14870, + 49206, + 40151, + 14843, + 47913, + 40207, + 14819, + 46625, + 40258, + 14789, + 45347, + 40113, + 14734, + 43861, + 39888, + 14666, + 42296, + 39648, + 14607, + 40671, + 39393, + 14694, + 38640, + 39153, + 14854, + 36514, + 39169, + 15596, + 33612, + 39224, + 16620, + 30553, + 39151, + 17899, + 27572, + 39229, + 19380, + 24503, + 39414, + 20798, + 22541, + 39581, + 22026, + 20769, + 39939, + 23299, + 18847, + 41804, + 14198, + 51229, + 42039, + 14331, + 49619, + 42120, + 14318, + 48313, + 42157, + 14275, + 47082, + 42187, + 14223, + 45862, + 42152, + 14170, + 44565, + 41980, + 14115, + 43082, + 41795, + 14058, + 41592, + 41604, + 14076, + 39859, + 41411, + 14168, + 37918, + 41277, + 14489, + 35656, + 41259, + 15185, + 32906, + 41398, + 16187, + 29928, + 41383, + 17524, + 27060, + 41442, + 18958, + 24203, + 41629, + 20262, + 22153, + 41863, + 21476, + 20258, + 43741, + 13707, + 51473, + 43921, + 13830, + 49971, + 44015, + 13842, + 48639, + 44028, + 13773, + 47440, + 44030, + 13689, + 46257, + 44026, + 13600, + 45081, + 43895, + 13551, + 43691, + 43750, + 13497, + 42293, + 43592, + 13437, + 40879, + 43441, + 13526, + 39005, + 43286, + 13619, + 37131, + 43200, + 14042, + 34809, + 43169, + 14629, + 32293, + 43243, + 15691, + 29466, + 43285, + 17036, + 26653, + 43396, + 18423, + 23939, + 43633, + 19710, + 21702, + 45858, + 13160, + 51755, + 45895, + 13337, + 50306, + 45997, + 13407, + 48957, + 45988, + 13314, + 47786, + 45961, + 13207, + 46630, + 45937, + 13085, + 45485, + 45857, + 13006, + 44232, + 45765, + 12947, + 42918, + 45659, + 12888, + 41600, + 45521, + 12902, + 40046, + 45427, + 12986, + 38229, + 45289, + 13145, + 36348, + 45203, + 13571, + 34127, + 45149, + 14192, + 31739, + 45200, + 15246, + 29055, + 45225, + 16565, + 26308, + 45377, + 17911, + 23624, + 5395, + 37956, + 35858, + 6009, + 38437, + 33737, + 6488, + 39049, + 31620, + 6879, + 39712, + 29521, + 7258, + 40448, + 27302, + 7580, + 41182, + 24892, + 8193, + 42103, + 23011, + 8711, + 42974, + 21082, + 9334, + 43721, + 19490, + 9986, + 44353, + 17923, + 10686, + 44853, + 16374, + 11453, + 45240, + 15383, + 12068, + 45665, + 14453, + 12825, + 45949, + 13567, + 13658, + 46141, + 12687, + 14387, + 46409, + 11755, + 15116, + 46689, + 10858, + 7582, + 35952, + 36937, + 8203, + 36425, + 34687, + 8757, + 36947, + 32476, + 9177, + 37635, + 30327, + 9562, + 38349, + 28167, + 9953, + 39177, + 25842, + 10401, + 40040, + 23591, + 11009, + 40962, + 21440, + 11597, + 41836, + 19688, + 12228, + 42616, + 18087, + 12849, + 43258, + 16504, + 13539, + 43710, + 15572, + 14275, + 44102, + 14686, + 15039, + 44438, + 13830, + 15818, + 44723, + 12987, + 16568, + 44980, + 12144, + 17180, + 45304, + 11266, + 9687, + 34133, + 38122, + 10375, + 34418, + 35897, + 10992, + 34769, + 33741, + 11429, + 35426, + 31470, + 11762, + 36219, + 29127, + 12197, + 37153, + 26789, + 12623, + 37986, + 24501, + 13102, + 38942, + 22423, + 13643, + 39849, + 20339, + 14366, + 40799, + 18648, + 14962, + 41581, + 16987, + 15561, + 42177, + 15856, + 16217, + 42678, + 14963, + 16900, + 43102, + 14109, + 17576, + 43471, + 13258, + 18227, + 43799, + 12385, + 18865, + 44107, + 11536, + 11883, + 32265, + 39384, + 12529, + 32501, + 37113, + 13222, + 32720, + 34990, + 13937, + 32896, + 32896, + 14209, + 33891, + 30493, + 14519, + 34798, + 28121, + 14922, + 35800, + 25756, + 15378, + 36798, + 23524, + 15799, + 37764, + 21432, + 16320, + 38746, + 19560, + 16931, + 39680, + 17882, + 17564, + 40492, + 16299, + 18198, + 41161, + 15365, + 18795, + 41693, + 14475, + 19390, + 42156, + 13590, + 19973, + 42564, + 12699, + 20584, + 42926, + 11810, + 14554, + 30139, + 40818, + 15191, + 30346, + 38628, + 15810, + 30574, + 36486, + 16316, + 30968, + 34306, + 16771, + 31488, + 32034, + 17039, + 32377, + 29574, + 17363, + 33399, + 27204, + 17708, + 34388, + 24888, + 18084, + 35485, + 22746, + 18528, + 36486, + 20665, + 19046, + 37548, + 19006, + 19580, + 38483, + 17344, + 20132, + 39289, + 15990, + 20708, + 40017, + 14986, + 21311, + 40671, + 14021, + 21871, + 41209, + 13088, + 22433, + 41657, + 12140, + 17449, + 27978, + 42299, + 18101, + 28153, + 40160, + 18624, + 28415, + 38021, + 19071, + 28819, + 35806, + 19425, + 29343, + 33586, + 19653, + 30057, + 31201, + 19848, + 30902, + 28746, + 20177, + 31964, + 26442, + 20575, + 32979, + 24203, + 20879, + 34149, + 22079, + 21269, + 35222, + 20117, + 21745, + 36330, + 18490, + 22262, + 37312, + 16824, + 22741, + 38138, + 15632, + 23245, + 38864, + 14594, + 23763, + 39515, + 13585, + 24308, + 40115, + 12566, + 20430, + 25787, + 43905, + 21199, + 26032, + 41789, + 21664, + 26308, + 39705, + 21976, + 26620, + 37590, + 22162, + 27137, + 35267, + 22392, + 27747, + 32912, + 22550, + 28652, + 30396, + 22699, + 29559, + 28018, + 22944, + 30602, + 25757, + 23275, + 31712, + 23572, + 23714, + 32877, + 21501, + 24060, + 34014, + 19639, + 24461, + 35131, + 17980, + 24928, + 36123, + 16375, + 25392, + 36988, + 15246, + 25818, + 37744, + 14121, + 26294, + 38448, + 12969, + 23393, + 23525, + 45560, + 24069, + 23820, + 43460, + 24811, + 24128, + 41408, + 25062, + 24517, + 39255, + 25189, + 24932, + 37104, + 25176, + 25592, + 34625, + 25237, + 26317, + 32150, + 25298, + 27247, + 29617, + 25505, + 28261, + 27288, + 25749, + 29266, + 25089, + 26027, + 30465, + 22966, + 26414, + 31602, + 20937, + 26827, + 32815, + 19175, + 27150, + 33941, + 17479, + 27538, + 34933, + 15982, + 27953, + 35829, + 14764, + 28411, + 36681, + 13498, + 26676, + 21555, + 47110, + 27412, + 21948, + 44890, + 27815, + 22237, + 42937, + 28271, + 22518, + 40978, + 28177, + 22909, + 38807, + 28144, + 23340, + 36591, + 28049, + 24044, + 34003, + 28011, + 24950, + 31345, + 28031, + 25907, + 28779, + 28208, + 26968, + 26512, + 28486, + 27998, + 24365, + 28798, + 29263, + 22369, + 29121, + 30374, + 20401, + 29461, + 31596, + 18689, + 29827, + 32757, + 16991, + 30153, + 33777, + 15531, + 30532, + 34718, + 14160, + 29814, + 19771, + 48605, + 30487, + 20117, + 46364, + 30961, + 20438, + 44268, + 31163, + 20729, + 42426, + 31312, + 21030, + 40524, + 31150, + 21409, + 38389, + 31013, + 21840, + 36145, + 30848, + 22442, + 33590, + 30738, + 23508, + 30735, + 30770, + 24655, + 27990, + 30937, + 25690, + 25776, + 31174, + 26812, + 23711, + 31479, + 28053, + 21778, + 31801, + 29234, + 19931, + 32091, + 30408, + 18234, + 32403, + 31516, + 16536, + 32767, + 32661, + 14991, + 33151, + 18384, + 49690, + 33457, + 18643, + 47750, + 33786, + 18904, + 45820, + 33947, + 19116, + 43953, + 34014, + 19309, + 42136, + 34014, + 19569, + 40184, + 33861, + 19928, + 38003, + 33730, + 20365, + 35646, + 33567, + 20846, + 33140, + 33467, + 22115, + 30162, + 33505, + 23352, + 27392, + 33702, + 24452, + 25049, + 33878, + 25661, + 23085, + 34146, + 26839, + 21198, + 34495, + 28096, + 19449, + 34768, + 29339, + 17719, + 35036, + 30575, + 15920, + 36091, + 17275, + 50386, + 36378, + 17504, + 48526, + 36567, + 17672, + 46888, + 36774, + 17857, + 45227, + 36724, + 17961, + 43501, + 36666, + 18071, + 41761, + 36527, + 18273, + 39808, + 36330, + 18568, + 37664, + 36209, + 19023, + 35172, + 36108, + 19591, + 32503, + 36153, + 20897, + 29500, + 36194, + 22226, + 26690, + 36388, + 23399, + 24350, + 36547, + 24637, + 22516, + 36795, + 25746, + 20701, + 37113, + 27056, + 18943, + 37456, + 28414, + 16977, + 38396, + 16378, + 51209, + 38840, + 16631, + 48994, + 38960, + 16723, + 47538, + 39086, + 16827, + 46074, + 39150, + 16912, + 44559, + 39054, + 16952, + 42920, + 38963, + 16996, + 41279, + 38790, + 17164, + 39318, + 38606, + 17381, + 37263, + 38534, + 18002, + 34584, + 38493, + 18807, + 31764, + 38580, + 19969, + 28858, + 38677, + 21354, + 25899, + 38817, + 22590, + 23785, + 39004, + 23781, + 21963, + 39297, + 24885, + 20074, + 39721, + 26309, + 18000, + 40539, + 15784, + 51293, + 40979, + 15957, + 49437, + 41107, + 15994, + 48038, + 41196, + 16021, + 46702, + 41287, + 16049, + 45370, + 41238, + 16065, + 43868, + 41145, + 16073, + 42313, + 41048, + 16091, + 40713, + 40917, + 16208, + 38791, + 40769, + 16336, + 36860, + 40753, + 17262, + 33928, + 40793, + 18224, + 31059, + 40904, + 19337, + 28215, + 41031, + 20687, + 25064, + 41154, + 21926, + 23280, + 41363, + 23068, + 21457, + 41745, + 24218, + 19358, + 42484, + 15186, + 51635, + 42780, + 15317, + 49957, + 42953, + 15372, + 48507, + 43030, + 15368, + 47229, + 43103, + 15364, + 45958, + 43130, + 15363, + 44626, + 43028, + 15373, + 43121, + 42926, + 15380, + 41615, + 42823, + 15448, + 39909, + 42725, + 15577, + 38034, + 42657, + 15877, + 35910, + 42667, + 16520, + 33272, + 42746, + 17555, + 30479, + 42841, + 18713, + 27689, + 42945, + 20056, + 24681, + 43180, + 21269, + 22749, + 43525, + 22576, + 20501, + 44429, + 14652, + 51878, + 44650, + 14768, + 50326, + 44825, + 14836, + 48880, + 44883, + 14808, + 47643, + 44935, + 14773, + 46417, + 44981, + 14736, + 45194, + 44911, + 14735, + 43795, + 44823, + 14741, + 42363, + 44730, + 14747, + 40925, + 44660, + 14872, + 39104, + 44593, + 15006, + 37287, + 44552, + 15381, + 35083, + 44543, + 15857, + 32692, + 44638, + 16988, + 29971, + 44759, + 18173, + 27245, + 44879, + 19478, + 24439, + 45187, + 20680, + 22097, + 46516, + 14049, + 52207, + 46663, + 14198, + 50760, + 46840, + 14358, + 49324, + 46887, + 14323, + 48101, + 46922, + 14266, + 46905, + 46952, + 14200, + 45715, + 46927, + 14160, + 44450, + 46853, + 14152, + 43081, + 46773, + 14142, + 41709, + 46692, + 14180, + 40178, + 46640, + 14277, + 38412, + 46575, + 14405, + 36633, + 46557, + 14846, + 34422, + 46568, + 15391, + 32110, + 46595, + 16345, + 29501, + 46681, + 17530, + 26790, + 46875, + 18826, + 24008, + 7108, + 39487, + 36963, + 7653, + 39993, + 34859, + 8111, + 40517, + 32798, + 8431, + 41122, + 30705, + 8718, + 41721, + 28639, + 9023, + 42490, + 26297, + 9317, + 43327, + 24117, + 9810, + 44131, + 22144, + 10328, + 44779, + 20208, + 11008, + 45301, + 18646, + 11567, + 45797, + 17030, + 12305, + 46132, + 15904, + 12663, + 46694, + 14965, + 13498, + 46889, + 14054, + 14584, + 46881, + 13189, + 15339, + 47050, + 12309, + 16095, + 47301, + 11443, + 9074, + 37680, + 37979, + 9751, + 38032, + 35776, + 10282, + 38473, + 33664, + 10702, + 39037, + 31527, + 11015, + 39698, + 29376, + 11343, + 40416, + 27030, + 11664, + 41092, + 24535, + 12202, + 42009, + 22539, + 12646, + 42809, + 20460, + 13289, + 43516, + 18896, + 13904, + 44104, + 17245, + 14592, + 44547, + 16012, + 15371, + 44883, + 15135, + 16139, + 45170, + 14289, + 16827, + 45445, + 13455, + 17468, + 45694, + 12597, + 18083, + 45971, + 11720, + 11045, + 35900, + 39139, + 11800, + 36118, + 36789, + 12335, + 36543, + 34604, + 12829, + 37023, + 32409, + 13238, + 37670, + 30212, + 13617, + 38381, + 27998, + 14003, + 39159, + 25642, + 14428, + 39998, + 23393, + 14935, + 40853, + 21199, + 15493, + 41678, + 19443, + 16067, + 42392, + 17825, + 16652, + 42978, + 16306, + 17341, + 43430, + 15448, + 18020, + 43821, + 14589, + 18683, + 44162, + 13730, + 19322, + 44461, + 12862, + 19959, + 44749, + 11962, + 13164, + 34063, + 40356, + 13911, + 34230, + 38066, + 14614, + 34443, + 35880, + 15213, + 34793, + 33702, + 15617, + 35440, + 31398, + 15943, + 36179, + 29047, + 16350, + 37053, + 26733, + 16748, + 37856, + 24493, + 17167, + 38792, + 22405, + 17633, + 39647, + 20333, + 18235, + 40536, + 18677, + 18825, + 41273, + 17025, + 19389, + 41870, + 15884, + 19972, + 42380, + 14980, + 20552, + 42819, + 14085, + 21128, + 43205, + 13193, + 21723, + 43567, + 12250, + 15381, + 32151, + 41532, + 16153, + 32269, + 39341, + 16912, + 32384, + 37218, + 17575, + 32656, + 35052, + 18247, + 32896, + 32896, + 18524, + 33835, + 30504, + 18829, + 34674, + 28158, + 19168, + 35596, + 25856, + 19534, + 36535, + 23644, + 19945, + 37482, + 21611, + 20399, + 38412, + 19771, + 20928, + 39307, + 18146, + 21490, + 40080, + 16503, + 22057, + 40775, + 15480, + 22582, + 41344, + 14535, + 23085, + 41823, + 13607, + 23636, + 42286, + 12584, + 18192, + 29995, + 42980, + 19050, + 30071, + 40824, + 19679, + 30290, + 38693, + 20295, + 30531, + 36547, + 20758, + 30962, + 34336, + 21167, + 31482, + 32068, + 21401, + 32317, + 29657, + 21706, + 33236, + 27363, + 22013, + 34139, + 25130, + 22354, + 35168, + 23014, + 22773, + 36137, + 20979, + 23222, + 37164, + 19272, + 23672, + 38102, + 17634, + 24145, + 38908, + 16146, + 24626, + 39598, + 15112, + 25125, + 40220, + 14086, + 25666, + 40809, + 13016, + 21106, + 27769, + 44494, + 21936, + 27910, + 42352, + 22640, + 28103, + 40229, + 23104, + 28413, + 38030, + 23512, + 28853, + 35787, + 23851, + 29383, + 33585, + 24069, + 30083, + 31266, + 24267, + 30893, + 28908, + 24534, + 31847, + 26674, + 24873, + 32773, + 24486, + 25206, + 33861, + 22434, + 25600, + 34844, + 20431, + 25982, + 35923, + 18779, + 26438, + 36878, + 17156, + 26824, + 37707, + 15795, + 27233, + 38442, + 14643, + 27714, + 39154, + 13405, + 24081, + 25407, + 46030, + 24884, + 25690, + 43904, + 25679, + 25972, + 41833, + 26119, + 26310, + 39700, + 26381, + 26695, + 37520, + 26568, + 27247, + 35203, + 26794, + 27834, + 32900, + 26949, + 28719, + 30465, + 27112, + 29574, + 28160, + 27360, + 30509, + 25988, + 27688, + 31501, + 23879, + 28073, + 32570, + 21898, + 28398, + 33602, + 19988, + 28705, + 34698, + 18297, + 29088, + 35658, + 16659, + 29496, + 36534, + 15345, + 29905, + 37356, + 13986, + 27245, + 23352, + 47736, + 28195, + 23710, + 45271, + 28746, + 23963, + 43251, + 29264, + 24203, + 41240, + 29381, + 24605, + 39081, + 29491, + 25049, + 36948, + 29513, + 25713, + 34558, + 29599, + 26417, + 32170, + 29704, + 27347, + 29680, + 29926, + 28305, + 27427, + 30171, + 29232, + 25311, + 30438, + 30284, + 23277, + 30774, + 31305, + 21333, + 31127, + 32361, + 19544, + 31403, + 33467, + 17830, + 31723, + 34452, + 16200, + 32114, + 35417, + 14702, + 30472, + 21405, + 49013, + 31187, + 21802, + 46768, + 31779, + 22144, + 44653, + 32083, + 22418, + 42753, + 32393, + 22694, + 40822, + 32369, + 23105, + 38683, + 32392, + 23559, + 36487, + 32365, + 24248, + 33966, + 32377, + 25102, + 31395, + 32446, + 26023, + 28862, + 32662, + 27006, + 26717, + 32928, + 27963, + 24653, + 33199, + 29103, + 22715, + 33511, + 30086, + 20795, + 33785, + 31213, + 19053, + 34061, + 32327, + 17334, + 34380, + 33482, + 15553, + 33677, + 19731, + 50315, + 34158, + 20070, + 47993, + 34611, + 20360, + 45931, + 34900, + 20656, + 44020, + 35077, + 20949, + 42211, + 35193, + 21272, + 40297, + 35146, + 21665, + 38208, + 35113, + 22115, + 35976, + 35061, + 22712, + 33480, + 35064, + 23748, + 30720, + 35140, + 24800, + 28090, + 35382, + 25790, + 25972, + 35644, + 26817, + 23975, + 35959, + 27896, + 22133, + 36359, + 28888, + 20305, + 36492, + 30146, + 18524, + 36707, + 31489, + 16462, + 36574, + 18579, + 51177, + 37034, + 18843, + 48833, + 37274, + 19041, + 47106, + 37544, + 19264, + 45351, + 37641, + 19453, + 43580, + 37735, + 19658, + 41797, + 37760, + 19938, + 39840, + 37728, + 20296, + 37732, + 37681, + 20744, + 35390, + 37618, + 21276, + 32897, + 37676, + 22484, + 30103, + 37790, + 23628, + 27480, + 38053, + 24671, + 25232, + 38265, + 25750, + 23349, + 38580, + 26779, + 21522, + 38978, + 27847, + 19680, + 39296, + 29213, + 17597, + 38860, + 17529, + 52147, + 39479, + 17886, + 49421, + 39692, + 18024, + 47846, + 39890, + 18168, + 46301, + 40061, + 18310, + 44727, + 40077, + 18428, + 43036, + 40117, + 18559, + 41339, + 40073, + 18788, + 39382, + 40024, + 19061, + 37343, + 40008, + 19617, + 34818, + 40027, + 20275, + 32183, + 40145, + 21436, + 29450, + 40275, + 22663, + 26832, + 40522, + 23719, + 24550, + 40771, + 24813, + 22707, + 41106, + 25809, + 20871, + 41617, + 27183, + 18565, + 41071, + 16746, + 52251, + 41622, + 17026, + 49923, + 41867, + 17149, + 48349, + 42006, + 17232, + 46946, + 42151, + 17320, + 45540, + 42187, + 17408, + 43999, + 42171, + 17495, + 42388, + 42162, + 17601, + 40736, + 42119, + 17813, + 38808, + 42088, + 18043, + 36866, + 42106, + 18728, + 34210, + 42176, + 19519, + 31536, + 42333, + 20463, + 28809, + 42458, + 21825, + 26161, + 42697, + 22958, + 23995, + 42993, + 24058, + 22048, + 43499, + 25231, + 19764, + 43133, + 16127, + 52094, + 43483, + 16253, + 50354, + 43754, + 16344, + 48768, + 43879, + 16383, + 47452, + 44009, + 16428, + 46142, + 44115, + 16484, + 44794, + 44065, + 16561, + 43247, + 44023, + 16645, + 41694, + 43996, + 16787, + 39993, + 43984, + 16991, + 38147, + 43994, + 17318, + 36109, + 44038, + 17907, + 33599, + 44147, + 18794, + 30960, + 44297, + 19774, + 28257, + 44441, + 21107, + 25527, + 44755, + 22265, + 23451, + 45253, + 23689, + 20726, + 45105, + 15565, + 52334, + 45369, + 15663, + 50769, + 45650, + 15777, + 49241, + 45771, + 15795, + 47967, + 45888, + 15812, + 46704, + 46004, + 15828, + 45439, + 46005, + 15868, + 44021, + 45958, + 15920, + 42530, + 45907, + 15977, + 41031, + 45890, + 16116, + 39272, + 45881, + 16266, + 37502, + 45896, + 16621, + 35386, + 45953, + 17136, + 33028, + 46083, + 18132, + 30431, + 46223, + 19156, + 27767, + 46350, + 20435, + 24929, + 46791, + 21633, + 22558, + 47202, + 14883, + 52677, + 47407, + 15044, + 51231, + 47666, + 15233, + 49808, + 47812, + 15299, + 48501, + 47896, + 15290, + 47257, + 47979, + 15276, + 46015, + 48034, + 15268, + 44738, + 48002, + 15301, + 43308, + 47981, + 15332, + 41879, + 47945, + 15392, + 40353, + 47932, + 15493, + 38634, + 47914, + 15604, + 36924, + 47946, + 16021, + 34748, + 48004, + 16496, + 32485, + 48141, + 17359, + 30048, + 48156, + 18493, + 27304, + 48360, + 19797, + 24374, + 8887, + 41125, + 38155, + 9453, + 41488, + 36046, + 9837, + 41947, + 34018, + 10141, + 42468, + 31959, + 10363, + 43105, + 29850, + 10588, + 43783, + 27656, + 10814, + 44519, + 25379, + 11189, + 45192, + 23252, + 11403, + 45839, + 21107, + 11890, + 46363, + 19470, + 12333, + 46884, + 17962, + 13205, + 47060, + 16498, + 13470, + 47641, + 15505, + 14660, + 47561, + 14579, + 15529, + 47657, + 13682, + 16294, + 47783, + 12809, + 16984, + 47967, + 11908, + 10788, + 39360, + 39207, + 11484, + 39600, + 36933, + 11944, + 40005, + 34829, + 12366, + 40395, + 32723, + 12621, + 41092, + 30553, + 12858, + 41772, + 28402, + 13172, + 42435, + 25998, + 13530, + 43160, + 23751, + 13911, + 43879, + 21567, + 14409, + 44499, + 19685, + 15039, + 45010, + 18067, + 15612, + 45426, + 16502, + 16347, + 45727, + 15605, + 17049, + 46000, + 14772, + 17724, + 46238, + 13927, + 18361, + 46451, + 13060, + 18974, + 46649, + 12166, + 12521, + 37643, + 40443, + 13339, + 37821, + 37922, + 13968, + 38118, + 35718, + 14458, + 38547, + 33569, + 14844, + 39125, + 31396, + 15124, + 39795, + 29232, + 15427, + 40493, + 26861, + 15739, + 41153, + 24408, + 16228, + 41946, + 22331, + 16663, + 42636, + 20269, + 17253, + 43289, + 18695, + 17824, + 43832, + 17060, + 18470, + 44261, + 15917, + 19144, + 44615, + 15050, + 19796, + 44920, + 14185, + 20392, + 45185, + 13317, + 20958, + 45445, + 12420, + 14427, + 35912, + 41321, + 15251, + 36019, + 39022, + 16065, + 36118, + 36796, + 16551, + 36566, + 34543, + 17013, + 37071, + 32301, + 17433, + 37686, + 30120, + 17799, + 38367, + 27932, + 18135, + 39108, + 25613, + 18511, + 39901, + 23391, + 18969, + 40692, + 21259, + 19497, + 41454, + 19507, + 20042, + 42117, + 17935, + 20582, + 42672, + 16374, + 21155, + 43153, + 15472, + 21725, + 43564, + 14575, + 22283, + 43929, + 13680, + 22865, + 44278, + 12715, + 16455, + 34087, + 42438, + 17417, + 34126, + 40220, + 18251, + 34215, + 38071, + 18981, + 34421, + 35901, + 19555, + 34794, + 33703, + 19953, + 35403, + 31414, + 20275, + 36068, + 29087, + 20594, + 36879, + 26784, + 20900, + 37657, + 24563, + 21321, + 38560, + 22569, + 21762, + 39374, + 20542, + 22281, + 40224, + 18909, + 22831, + 40958, + 17245, + 23313, + 41556, + 15983, + 23800, + 42073, + 15046, + 24283, + 42529, + 14111, + 24821, + 42977, + 13057, + 18905, + 32107, + 43738, + 19874, + 32133, + 41544, + 20652, + 32243, + 39400, + 21402, + 32360, + 37241, + 22000, + 32642, + 35058, + 22551, + 32896, + 32896, + 22843, + 33786, + 30565, + 23129, + 34570, + 28276, + 23431, + 35399, + 26057, + 23775, + 36230, + 23904, + 24207, + 37165, + 21906, + 24625, + 38075, + 20031, + 25050, + 38969, + 18401, + 25512, + 39732, + 16739, + 25983, + 40387, + 15600, + 26470, + 40980, + 14551, + 26922, + 41491, + 13488, + 21834, + 29846, + 45165, + 22724, + 29935, + 42997, + 23606, + 30029, + 40895, + 24203, + 30278, + 38703, + 24775, + 30542, + 36505, + 25141, + 30979, + 34322, + 25462, + 31492, + 32100, + 25718, + 32285, + 29787, + 26021, + 33135, + 27559, + 26312, + 33985, + 25387, + 26642, + 34907, + 23311, + 27050, + 35828, + 21308, + 27473, + 36775, + 19532, + 27830, + 37694, + 17925, + 28218, + 38477, + 16357, + 28615, + 39187, + 15178, + 29069, + 39839, + 13951, + 24721, + 27506, + 46607, + 25630, + 27702, + 44412, + 26458, + 27876, + 42318, + 27103, + 28107, + 40181, + 27522, + 28451, + 37982, + 27889, + 28894, + 35775, + 28156, + 29405, + 33589, + 28348, + 30104, + 31313, + 28575, + 30899, + 29025, + 28881, + 31771, + 26870, + 29270, + 32634, + 24753, + 29546, + 33607, + 22756, + 29881, + 34504, + 20771, + 30197, + 35524, + 19045, + 30583, + 36447, + 17432, + 30968, + 37269, + 15943, + 31327, + 38038, + 14552, + 27922, + 25278, + 48108, + 28847, + 25571, + 45823, + 29519, + 25806, + 43726, + 30109, + 26031, + 41684, + 30441, + 26378, + 39574, + 30697, + 26774, + 37425, + 30875, + 27328, + 35158, + 31078, + 27908, + 32911, + 31259, + 28780, + 30527, + 31439, + 29610, + 28271, + 31684, + 30491, + 26179, + 32000, + 31385, + 24128, + 32357, + 32320, + 22221, + 32717, + 33177, + 20336, + 32925, + 34270, + 18595, + 33244, + 35230, + 16940, + 33633, + 36154, + 15364, + 31051, + 23175, + 49513, + 31802, + 23512, + 47268, + 32547, + 23834, + 45107, + 32967, + 24093, + 43121, + 33403, + 24356, + 41102, + 33535, + 24784, + 38994, + 33699, + 25214, + 36898, + 33761, + 25862, + 34537, + 33875, + 26546, + 32195, + 34019, + 27432, + 29763, + 34266, + 28344, + 27576, + 34513, + 29238, + 25528, + 34791, + 30164, + 23553, + 35135, + 31097, + 21669, + 35468, + 32082, + 19850, + 35663, + 33186, + 18099, + 35889, + 34232, + 16295, + 34209, + 21243, + 50762, + 34813, + 21674, + 48404, + 35334, + 22019, + 46386, + 35767, + 22338, + 44438, + 36035, + 22622, + 42533, + 36282, + 22928, + 40566, + 36374, + 23328, + 38498, + 36496, + 23776, + 36354, + 36555, + 24468, + 33872, + 36608, + 25269, + 31404, + 36709, + 26140, + 28949, + 36977, + 27091, + 26852, + 37289, + 28010, + 24836, + 37631, + 28961, + 22978, + 37979, + 29863, + 21106, + 38205, + 30983, + 19260, + 38431, + 32190, + 17305, + 37111, + 19864, + 51652, + 37645, + 20110, + 49249, + 37981, + 20345, + 47420, + 38358, + 20637, + 45583, + 38604, + 20942, + 43788, + 38808, + 21231, + 41972, + 38940, + 21557, + 40039, + 38991, + 21934, + 37998, + 39044, + 22408, + 35778, + 39086, + 23041, + 33330, + 39186, + 24010, + 30698, + 39324, + 24965, + 28203, + 39585, + 25920, + 26147, + 39899, + 26843, + 24188, + 40302, + 27778, + 22349, + 40846, + 28578, + 20482, + 41020, + 29948, + 18421, + 39481, + 18717, + 52558, + 40095, + 19033, + 50037, + 40460, + 19245, + 48249, + 40737, + 19435, + 46621, + 41022, + 19640, + 44972, + 41142, + 19825, + 43219, + 41294, + 20028, + 41447, + 41370, + 20304, + 39482, + 41424, + 20626, + 37452, + 41436, + 21154, + 35127, + 41470, + 21781, + 32696, + 41598, + 22863, + 30073, + 41788, + 23902, + 27580, + 42066, + 24868, + 25426, + 42354, + 25875, + 23501, + 42786, + 26860, + 21596, + 43420, + 27918, + 19475, + 41737, + 17887, + 52648, + 42235, + 18131, + 50572, + 42619, + 18320, + 48804, + 42821, + 18449, + 47312, + 43039, + 18591, + 45809, + 43180, + 18738, + 44214, + 43248, + 18888, + 42536, + 43335, + 19055, + 40826, + 43383, + 19314, + 38901, + 43455, + 19590, + 36965, + 43511, + 20110, + 34497, + 43594, + 20763, + 31983, + 43739, + 21797, + 29427, + 43956, + 22966, + 26987, + 44300, + 23965, + 24774, + 44657, + 25045, + 22750, + 45183, + 26169, + 20585, + 43791, + 17156, + 52746, + 44191, + 17329, + 50943, + 44566, + 17478, + 49243, + 44751, + 17568, + 47845, + 44939, + 17663, + 46455, + 45140, + 17770, + 45057, + 45163, + 17899, + 43456, + 45200, + 18034, + 41842, + 45254, + 18214, + 40126, + 45323, + 18449, + 38288, + 45402, + 18751, + 36328, + 45470, + 19255, + 33917, + 45590, + 19966, + 31394, + 45761, + 20785, + 28804, + 45983, + 22148, + 26376, + 46343, + 23218, + 24126, + 46855, + 24394, + 21679, + 45761, + 16410, + 52849, + 46092, + 16545, + 51290, + 46461, + 16724, + 49788, + 46708, + 16837, + 48410, + 46875, + 16907, + 47090, + 47044, + 16982, + 45759, + 47142, + 17069, + 44325, + 47166, + 17173, + 42777, + 47199, + 17282, + 41222, + 47237, + 17447, + 39495, + 47285, + 17630, + 37723, + 47341, + 17987, + 35679, + 47401, + 18501, + 33360, + 47520, + 19274, + 30872, + 47691, + 20128, + 28282, + 47923, + 21403, + 25756, + 48372, + 22532, + 23285, + 47841, + 15704, + 53117, + 48103, + 15879, + 51691, + 48427, + 16056, + 50320, + 48735, + 16220, + 49000, + 48870, + 16252, + 47707, + 49006, + 16289, + 46407, + 49140, + 16327, + 45105, + 49189, + 16385, + 43624, + 49244, + 16446, + 42135, + 49263, + 16542, + 40588, + 49283, + 16662, + 38897, + 49291, + 16804, + 37189, + 49377, + 17217, + 35099, + 49445, + 17718, + 32878, + 49709, + 18355, + 30622, + 49664, + 19449, + 27822, + 49856, + 20681, + 25047, + 10853, + 42727, + 39543, + 11388, + 43015, + 37358, + 11710, + 43436, + 35269, + 11952, + 43892, + 33195, + 12126, + 44497, + 31079, + 12249, + 45117, + 28967, + 12423, + 45725, + 26757, + 12388, + 46450, + 24536, + 12592, + 47019, + 22404, + 12843, + 47496, + 20360, + 13322, + 47947, + 18895, + 14216, + 48029, + 17310, + 14537, + 48475, + 16026, + 15656, + 48390, + 15069, + 16470, + 48477, + 14160, + 17179, + 48578, + 13280, + 17863, + 48662, + 12370, + 12590, + 41036, + 40451, + 13215, + 41274, + 38180, + 13742, + 41578, + 36014, + 14114, + 42001, + 33911, + 14396, + 42540, + 31802, + 14598, + 43186, + 29697, + 14826, + 43827, + 27451, + 15059, + 44464, + 25072, + 15348, + 45067, + 22784, + 15558, + 45559, + 20563, + 16122, + 45994, + 19001, + 16667, + 46361, + 17414, + 17282, + 46644, + 16132, + 17989, + 46866, + 15287, + 18660, + 47061, + 14420, + 19292, + 47230, + 13540, + 19896, + 47377, + 12629, + 14133, + 39374, + 41653, + 14982, + 39535, + 39098, + 15720, + 39695, + 36873, + 16151, + 40159, + 34700, + 16534, + 40666, + 32524, + 16775, + 41299, + 30411, + 17018, + 41912, + 28277, + 17312, + 42513, + 25853, + 17646, + 43153, + 23588, + 17989, + 43758, + 21385, + 18465, + 44319, + 19562, + 19037, + 44794, + 17954, + 19592, + 45174, + 16376, + 20217, + 45464, + 15541, + 20807, + 45727, + 14687, + 21367, + 45977, + 13821, + 21935, + 46213, + 12891, + 15855, + 37746, + 42384, + 16731, + 37808, + 40057, + 17596, + 37893, + 37841, + 18245, + 38185, + 35643, + 18700, + 38650, + 33454, + 19046, + 39228, + 31283, + 19304, + 39853, + 29154, + 19562, + 40504, + 26805, + 19832, + 41111, + 24379, + 20314, + 41847, + 22385, + 20734, + 42488, + 20372, + 21268, + 43090, + 18803, + 21791, + 43592, + 17180, + 22328, + 44021, + 15955, + 22882, + 44396, + 15055, + 23423, + 44734, + 14155, + 24013, + 45078, + 13121, + 17766, + 35983, + 43489, + 18760, + 35995, + 41158, + 19631, + 36069, + 38959, + 20471, + 36165, + 36768, + 20953, + 36580, + 34545, + 21375, + 37043, + 32316, + 21721, + 37624, + 30123, + 22024, + 38267, + 27954, + 22320, + 38963, + 25691, + 22684, + 39711, + 23535, + 23124, + 40486, + 21490, + 23593, + 41220, + 19683, + 24065, + 41869, + 18117, + 24534, + 42417, + 16524, + 25000, + 42887, + 15548, + 25476, + 43312, + 14588, + 26006, + 43735, + 13487, + 20063, + 34074, + 44637, + 21039, + 34088, + 42413, + 21950, + 34118, + 40242, + 22769, + 34186, + 38067, + 23443, + 34380, + 35885, + 23903, + 34750, + 33689, + 24243, + 35345, + 31433, + 24518, + 36010, + 29160, + 24820, + 36715, + 26954, + 25145, + 37393, + 24813, + 25550, + 38286, + 22821, + 25966, + 39104, + 20808, + 26382, + 39918, + 19139, + 26833, + 40618, + 17528, + 27276, + 41204, + 16126, + 27692, + 41737, + 15080, + 28145, + 42243, + 13937, + 22591, + 32029, + 45952, + 23553, + 32080, + 43710, + 24470, + 32129, + 41605, + 25260, + 32207, + 39419, + 25977, + 32316, + 37218, + 26392, + 32622, + 35048, + 26757, + 32896, + 32896, + 27072, + 33745, + 30646, + 27376, + 34481, + 28433, + 27680, + 35263, + 26272, + 28000, + 36013, + 24144, + 28433, + 36881, + 22153, + 28839, + 37737, + 20243, + 29174, + 38599, + 18658, + 29563, + 39320, + 17083, + 29975, + 39968, + 15724, + 30423, + 40581, + 14456, + 25468, + 29668, + 47281, + 26455, + 29780, + 45044, + 27325, + 29876, + 42932, + 28132, + 29989, + 40804, + 28679, + 30265, + 38646, + 29174, + 30557, + 36490, + 29417, + 30989, + 34319, + 29655, + 31498, + 32124, + 29934, + 32263, + 29880, + 30271, + 33062, + 27715, + 30594, + 33884, + 25597, + 30909, + 34711, + 23553, + 31261, + 35554, + 21563, + 31623, + 36431, + 19724, + 31977, + 37307, + 18146, + 32342, + 38052, + 16609, + 32707, + 38774, + 15149, + 28571, + 27389, + 48762, + 29476, + 27566, + 46463, + 30264, + 27726, + 44301, + 30932, + 27896, + 42203, + 31470, + 28139, + 40071, + 31865, + 28476, + 37897, + 32189, + 28930, + 35724, + 32405, + 29459, + 33590, + 32588, + 30150, + 31357, + 32822, + 30927, + 29106, + 33109, + 31758, + 27008, + 33473, + 32569, + 24954, + 33796, + 33418, + 23016, + 34135, + 34239, + 21067, + 34405, + 35188, + 19283, + 34707, + 36109, + 17652, + 35083, + 36987, + 16025, + 31568, + 24992, + 50072, + 32427, + 25335, + 47814, + 33223, + 25642, + 45681, + 33810, + 25908, + 43614, + 34319, + 26151, + 41550, + 34628, + 26499, + 39457, + 34904, + 26868, + 37351, + 35091, + 27420, + 35119, + 35298, + 28001, + 32915, + 35498, + 28825, + 30595, + 35686, + 29623, + 28378, + 35952, + 30463, + 26352, + 36294, + 31264, + 24364, + 36666, + 32164, + 22504, + 37065, + 33029, + 20620, + 37196, + 34056, + 18816, + 37420, + 35040, + 17030, + 34711, + 23033, + 51387, + 35475, + 23379, + 48891, + 36075, + 23699, + 46885, + 36656, + 24011, + 44919, + 37013, + 24284, + 42894, + 37375, + 24561, + 40828, + 37568, + 24965, + 38828, + 37789, + 25354, + 36809, + 37878, + 26001, + 34483, + 38014, + 26672, + 32190, + 38191, + 27511, + 29830, + 38473, + 28366, + 27692, + 38767, + 29205, + 25708, + 39095, + 30042, + 23772, + 39481, + 30932, + 21884, + 39853, + 31885, + 20003, + 40093, + 33002, + 18063, + 37632, + 21279, + 52369, + 38278, + 21630, + 49898, + 38764, + 21952, + 47944, + 39226, + 22274, + 46081, + 39601, + 22570, + 44204, + 39869, + 22846, + 42293, + 40105, + 23152, + 40328, + 40258, + 23524, + 38313, + 40428, + 23971, + 36202, + 40555, + 24639, + 33818, + 40647, + 25418, + 31433, + 40816, + 26264, + 29047, + 41094, + 27149, + 27006, + 41443, + 27981, + 25060, + 41900, + 28844, + 23154, + 42308, + 29756, + 21191, + 42613, + 30882, + 19142, + 40117, + 19784, + 53174, + 40733, + 20089, + 50771, + 41220, + 20383, + 48759, + 41570, + 20671, + 47036, + 41945, + 21008, + 45289, + 42165, + 21283, + 43493, + 42376, + 21549, + 41667, + 42517, + 21867, + 39741, + 42633, + 22220, + 37763, + 42737, + 22712, + 35560, + 42851, + 23325, + 33197, + 43034, + 24223, + 30678, + 43237, + 25132, + 28302, + 43523, + 26056, + 26294, + 43892, + 26939, + 24332, + 44461, + 27897, + 22370, + 45210, + 28809, + 20291, + 42343, + 18927, + 53443, + 42908, + 19206, + 51253, + 43401, + 19420, + 49378, + 43699, + 19599, + 47766, + 43994, + 19793, + 46162, + 44253, + 19993, + 44498, + 44410, + 20182, + 42757, + 44594, + 20382, + 40997, + 44708, + 20692, + 39100, + 44825, + 21034, + 37203, + 44901, + 21566, + 34901, + 45012, + 22170, + 32546, + 45206, + 23128, + 30046, + 45493, + 24117, + 27665, + 45838, + 25053, + 25554, + 46203, + 26055, + 23529, + 46735, + 27128, + 21336, + 44456, + 18207, + 53546, + 44953, + 18414, + 51587, + 45382, + 18569, + 49897, + 45705, + 18721, + 48351, + 45947, + 18868, + 46880, + 46195, + 19022, + 45389, + 46331, + 19180, + 43773, + 46449, + 19344, + 42115, + 46574, + 19533, + 40395, + 46687, + 19773, + 38549, + 46812, + 20045, + 36650, + 46879, + 20554, + 34285, + 46980, + 21247, + 31917, + 47203, + 22156, + 29460, + 47507, + 23231, + 27081, + 47926, + 24187, + 24816, + 48451, + 25342, + 22408, + 46520, + 17431, + 53478, + 46928, + 17619, + 51905, + 47330, + 17802, + 50400, + 47706, + 17984, + 48940, + 47899, + 18086, + 47540, + 48098, + 18194, + 46130, + 48272, + 18307, + 44674, + 48364, + 18439, + 43088, + 48468, + 18576, + 41497, + 48560, + 18745, + 39780, + 48653, + 18936, + 37992, + 48740, + 19250, + 36021, + 48796, + 19766, + 33737, + 48923, + 20421, + 31322, + 49161, + 21242, + 28876, + 49477, + 22428, + 26460, + 49936, + 23500, + 23947, + 48381, + 16523, + 53593, + 48820, + 16776, + 52166, + 49235, + 17003, + 50832, + 49677, + 17150, + 49561, + 49924, + 17192, + 48250, + 50095, + 17235, + 46902, + 50266, + 17284, + 45544, + 50391, + 17360, + 44079, + 50556, + 17415, + 42585, + 50583, + 17570, + 41008, + 50665, + 17721, + 39277, + 50748, + 17890, + 37521, + 50927, + 18183, + 35584, + 51005, + 18676, + 33404, + 51204, + 19373, + 31106, + 51204, + 20378, + 28314, + 51424, + 21666, + 25607, + 13085, + 44204, + 40825, + 13449, + 44553, + 38646, + 13731, + 44936, + 36514, + 13899, + 45395, + 34439, + 13977, + 45885, + 32363, + 14061, + 46434, + 30296, + 14126, + 46969, + 28196, + 14185, + 47574, + 25995, + 14116, + 48220, + 23814, + 14334, + 48562, + 21636, + 14658, + 48900, + 19782, + 15302, + 49064, + 18195, + 15832, + 49222, + 16529, + 16487, + 49367, + 15548, + 17359, + 49358, + 14676, + 18069, + 49409, + 13779, + 18726, + 49464, + 12843, + 14566, + 42651, + 41694, + 15183, + 42854, + 39435, + 15698, + 43110, + 37277, + 16030, + 43532, + 35153, + 16267, + 43996, + 33032, + 16454, + 44610, + 30938, + 16591, + 45228, + 28857, + 16721, + 45779, + 26538, + 16834, + 46322, + 24277, + 16986, + 46742, + 22043, + 17253, + 47113, + 20036, + 17804, + 47392, + 18503, + 18324, + 47603, + 16884, + 18977, + 47769, + 15843, + 19642, + 47905, + 14955, + 20266, + 48029, + 14051, + 20849, + 48164, + 13120, + 15899, + 41169, + 42595, + 16696, + 41243, + 40203, + 17413, + 41398, + 38013, + 17962, + 41706, + 35850, + 18301, + 42181, + 33702, + 18548, + 42732, + 31588, + 18728, + 43357, + 29511, + 18936, + 43964, + 27251, + 19162, + 44537, + 24892, + 19438, + 45052, + 22662, + 19679, + 45467, + 20515, + 20217, + 45824, + 18983, + 20726, + 46108, + 17406, + 21238, + 46356, + 16127, + 21808, + 46592, + 15258, + 22356, + 46816, + 14362, + 22915, + 47015, + 13411, + 17533, + 39533, + 43577, + 18451, + 39527, + 41116, + 19245, + 39634, + 38906, + 20006, + 39769, + 36718, + 20375, + 40283, + 34516, + 20668, + 40834, + 32330, + 20901, + 41398, + 30268, + 21123, + 41974, + 28150, + 21401, + 42513, + 25778, + 21733, + 43101, + 23598, + 22071, + 43681, + 21506, + 22484, + 44189, + 19671, + 22992, + 44619, + 18069, + 23491, + 44975, + 16459, + 24002, + 45287, + 15567, + 24489, + 45569, + 14681, + 25017, + 45841, + 13665, + 19264, + 37841, + 44551, + 20239, + 37838, + 42171, + 21164, + 37843, + 39928, + 22012, + 37898, + 37746, + 22591, + 38182, + 35560, + 22954, + 38630, + 33365, + 23236, + 39187, + 31215, + 23460, + 39786, + 29123, + 23736, + 40388, + 26854, + 24034, + 40944, + 24495, + 24475, + 41672, + 22554, + 24835, + 42306, + 20557, + 25279, + 42879, + 18987, + 25723, + 43352, + 17418, + 26170, + 43761, + 16082, + 26638, + 44154, + 15092, + 27152, + 44539, + 13974, + 21303, + 36047, + 45600, + 22340, + 35988, + 43315, + 23318, + 35941, + 41117, + 24167, + 35986, + 38930, + 24949, + 36065, + 36739, + 25299, + 36493, + 34505, + 25616, + 36977, + 32268, + 25913, + 37551, + 30141, + 26194, + 38152, + 28035, + 26503, + 38789, + 25855, + 26844, + 39489, + 23721, + 27253, + 40259, + 21697, + 27665, + 40970, + 19845, + 28069, + 41569, + 18357, + 28471, + 42074, + 16841, + 28876, + 42560, + 15655, + 29329, + 43054, + 14452, + 23727, + 34061, + 46837, + 24739, + 34049, + 44535, + 25689, + 34013, + 42386, + 26559, + 34016, + 40215, + 27284, + 34113, + 38031, + 27801, + 34352, + 35857, + 28109, + 34743, + 33670, + 28411, + 35324, + 31473, + 28704, + 35938, + 29300, + 29001, + 36596, + 27139, + 29304, + 37244, + 25001, + 29691, + 38057, + 23014, + 30081, + 38838, + 21028, + 30449, + 39594, + 19369, + 30853, + 40242, + 17829, + 31292, + 40794, + 16320, + 31695, + 41386, + 15011, + 26337, + 31924, + 48094, + 27356, + 31936, + 45817, + 28285, + 31943, + 43652, + 29134, + 31975, + 41490, + 29800, + 32124, + 39337, + 30403, + 32300, + 37189, + 30676, + 32618, + 35037, + 30905, + 32896, + 32896, + 31246, + 33716, + 30699, + 31573, + 34420, + 28534, + 31903, + 35171, + 26428, + 32239, + 35861, + 24334, + 32599, + 36655, + 22329, + 32939, + 37429, + 20385, + 33260, + 38252, + 18867, + 33614, + 38963, + 17352, + 34010, + 39649, + 15792, + 29298, + 29566, + 49401, + 30243, + 29652, + 47155, + 31130, + 29710, + 44986, + 31879, + 29833, + 42853, + 32581, + 29971, + 40711, + 33044, + 30257, + 38571, + 33450, + 30568, + 36434, + 33636, + 31015, + 34304, + 33829, + 31521, + 32144, + 34108, + 32270, + 29934, + 34440, + 33030, + 27819, + 34779, + 33786, + 25779, + 35114, + 34533, + 23776, + 35490, + 35317, + 21815, + 35862, + 36119, + 19940, + 36131, + 37004, + 18343, + 36377, + 37879, + 16703, + 32252, + 27243, + 50695, + 33152, + 27461, + 48445, + 33963, + 27614, + 46309, + 34667, + 27775, + 44192, + 35249, + 27955, + 42074, + 35713, + 28204, + 39948, + 36085, + 28514, + 37824, + 36380, + 28967, + 35692, + 36582, + 29503, + 33588, + 36764, + 30174, + 31400, + 36995, + 30927, + 29197, + 37305, + 31700, + 27161, + 37692, + 32434, + 25188, + 38043, + 33261, + 23260, + 38398, + 34084, + 21326, + 38679, + 34961, + 19488, + 38946, + 35882, + 17709, + 35282, + 24872, + 51921, + 36186, + 25197, + 49485, + 36878, + 25517, + 47464, + 37512, + 25790, + 45479, + 37976, + 26041, + 43422, + 38399, + 26284, + 41349, + 38684, + 26627, + 39306, + 38959, + 26981, + 37255, + 39139, + 27532, + 35058, + 39336, + 28097, + 32892, + 39571, + 28878, + 30633, + 39809, + 29627, + 28472, + 40116, + 30411, + 26512, + 40496, + 31151, + 24557, + 40915, + 32036, + 22657, + 41322, + 32922, + 20743, + 41547, + 33901, + 18780, + 38258, + 23025, + 53001, + 38969, + 23305, + 50646, + 39605, + 23575, + 48566, + 40149, + 23861, + 46636, + 40662, + 24135, + 44664, + 40986, + 24405, + 42662, + 41278, + 24704, + 40645, + 41471, + 25102, + 38677, + 41669, + 25508, + 36666, + 41780, + 26139, + 34409, + 41935, + 26793, + 32172, + 42172, + 27603, + 29883, + 42504, + 28400, + 27800, + 42836, + 29193, + 25860, + 43209, + 30006, + 23898, + 43621, + 30917, + 21919, + 44013, + 31892, + 19879, + 40819, + 20968, + 53796, + 41446, + 21527, + 51522, + 42053, + 21941, + 49424, + 42491, + 22259, + 47585, + 42906, + 22553, + 45758, + 43220, + 22819, + 43895, + 43487, + 23077, + 41998, + 43714, + 23377, + 40059, + 43898, + 23730, + 38086, + 44085, + 24175, + 35974, + 44260, + 24771, + 33691, + 44399, + 25552, + 31398, + 44627, + 26399, + 29117, + 44968, + 27258, + 27109, + 45379, + 28072, + 25160, + 45902, + 28958, + 23146, + 46307, + 29945, + 20984, + 43029, + 20005, + 54075, + 43629, + 20262, + 51976, + 44182, + 20472, + 50090, + 44655, + 20751, + 48325, + 45016, + 21067, + 46627, + 45349, + 21370, + 44910, + 45547, + 21627, + 43134, + 45763, + 21881, + 41340, + 45924, + 22199, + 39460, + 46078, + 22537, + 37550, + 46212, + 23029, + 35377, + 46364, + 23599, + 33094, + 46626, + 24436, + 30666, + 46889, + 25304, + 28403, + 47238, + 26212, + 26370, + 47679, + 27065, + 24358, + 48207, + 28118, + 22126, + 45175, + 19254, + 54220, + 45756, + 19479, + 52265, + 46234, + 19649, + 50568, + 46680, + 19826, + 48923, + 46955, + 20007, + 47367, + 47241, + 20196, + 45790, + 47466, + 20384, + 44150, + 47650, + 20584, + 42461, + 47842, + 20826, + 40746, + 47982, + 21123, + 38891, + 48131, + 21427, + 37027, + 48216, + 21989, + 34786, + 48345, + 22594, + 32504, + 48646, + 23438, + 30090, + 49031, + 24330, + 27729, + 49422, + 25262, + 25569, + 49927, + 26431, + 23057, + 47283, + 18425, + 54202, + 47790, + 18671, + 52506, + 48223, + 18862, + 50983, + 48658, + 19059, + 49490, + 48923, + 19202, + 48022, + 49149, + 19336, + 46549, + 49391, + 19446, + 45086, + 49549, + 19583, + 43487, + 49716, + 19728, + 41875, + 49871, + 19903, + 40167, + 50008, + 20117, + 38333, + 50144, + 20407, + 36401, + 50214, + 20985, + 34189, + 50363, + 21663, + 31907, + 50645, + 22506, + 29486, + 50990, + 23513, + 27055, + 51463, + 24555, + 24524, + 49262, + 17544, + 54287, + 49780, + 17808, + 52746, + 50230, + 17949, + 51430, + 50662, + 18039, + 50150, + 51032, + 18093, + 48863, + 51224, + 18172, + 47456, + 51419, + 18256, + 46039, + 51596, + 18359, + 44570, + 51740, + 18490, + 43004, + 51883, + 18637, + 41425, + 52008, + 18828, + 39684, + 52106, + 19064, + 37846, + 52233, + 19415, + 35875, + 52346, + 19924, + 33733, + 52483, + 20656, + 31370, + 52689, + 21547, + 28911, + 52991, + 22738, + 26179, + 15163, + 45830, + 42199, + 15617, + 46061, + 39979, + 15802, + 46426, + 37874, + 15906, + 46840, + 35769, + 15926, + 47285, + 33654, + 15937, + 47768, + 31562, + 15925, + 48261, + 29481, + 15884, + 48835, + 27340, + 15900, + 49385, + 25260, + 15871, + 49734, + 23044, + 15744, + 50020, + 20697, + 16294, + 50109, + 19080, + 16648, + 50291, + 17492, + 17465, + 50212, + 16189, + 18211, + 50247, + 15283, + 18909, + 50275, + 14345, + 19597, + 50275, + 13368, + 16640, + 44304, + 42986, + 17316, + 44388, + 40628, + 17680, + 44683, + 38475, + 17972, + 45036, + 36324, + 18108, + 45490, + 34197, + 18209, + 45980, + 32092, + 18276, + 46538, + 30038, + 18344, + 47063, + 27923, + 18449, + 47596, + 25749, + 18554, + 48042, + 23595, + 18661, + 48359, + 21387, + 19027, + 48553, + 19599, + 19542, + 48650, + 18002, + 20039, + 48704, + 16465, + 20668, + 48801, + 15542, + 21244, + 48920, + 14616, + 21823, + 49022, + 13655, + 17921, + 42805, + 43876, + 18731, + 42817, + 41401, + 19319, + 42999, + 39197, + 19847, + 43223, + 37028, + 20123, + 43689, + 34851, + 20340, + 44171, + 32663, + 20458, + 44831, + 30591, + 20542, + 45436, + 28541, + 20750, + 45893, + 26314, + 20937, + 46341, + 24162, + 21147, + 46692, + 22040, + 21420, + 46984, + 20097, + 21881, + 47185, + 18575, + 22301, + 47350, + 16986, + 22806, + 47519, + 15893, + 23346, + 47705, + 14962, + 23943, + 47873, + 13897, + 19210, + 41310, + 44775, + 20125, + 41291, + 42217, + 20937, + 41323, + 39928, + 21653, + 41439, + 37774, + 22119, + 41774, + 35618, + 22392, + 42242, + 33446, + 22604, + 42794, + 31358, + 22760, + 43413, + 29310, + 22978, + 43979, + 27102, + 23219, + 44503, + 24864, + 23475, + 45001, + 22727, + 23703, + 45394, + 20645, + 24164, + 45706, + 19098, + 24606, + 45963, + 17573, + 25025, + 46173, + 16214, + 25498, + 46409, + 15284, + 26017, + 46635, + 14207, + 20865, + 39688, + 45636, + 21884, + 39583, + 43219, + 22804, + 39508, + 40906, + 23589, + 39597, + 38739, + 24297, + 39747, + 36575, + 24554, + 40217, + 34381, + 24766, + 40729, + 32223, + 24980, + 41302, + 30182, + 25211, + 41866, + 28093, + 25510, + 42387, + 25825, + 25816, + 42957, + 23710, + 26122, + 43521, + 21693, + 26462, + 44004, + 19865, + 26892, + 44398, + 18329, + 27312, + 44725, + 16770, + 27762, + 45061, + 15644, + 28236, + 45375, + 14494, + 22840, + 37920, + 46661, + 23865, + 37800, + 44276, + 24785, + 37722, + 42035, + 25637, + 37710, + 39837, + 26371, + 37802, + 37660, + 26824, + 38128, + 35473, + 27127, + 38577, + 33285, + 27387, + 39108, + 31189, + 27607, + 39663, + 29152, + 27863, + 40231, + 26942, + 28110, + 40761, + 24614, + 28516, + 41474, + 22677, + 28866, + 42090, + 20741, + 29246, + 42612, + 19227, + 29634, + 43057, + 17748, + 30022, + 43457, + 16303, + 30486, + 43959, + 15005, + 24998, + 36036, + 47811, + 26103, + 35901, + 45428, + 27062, + 35813, + 43231, + 27983, + 35741, + 41060, + 28648, + 35863, + 38876, + 29214, + 36040, + 36685, + 29487, + 36478, + 34470, + 29767, + 36955, + 32271, + 30071, + 37470, + 30200, + 30349, + 38025, + 28140, + 30627, + 38638, + 26001, + 30914, + 39284, + 23899, + 31284, + 40017, + 21923, + 31666, + 40686, + 20077, + 32069, + 41246, + 18606, + 32459, + 41727, + 17165, + 32882, + 42307, + 15595, + 27487, + 33982, + 48961, + 28534, + 33911, + 46669, + 29515, + 33839, + 44472, + 30367, + 33825, + 42292, + 31107, + 33883, + 40120, + 31687, + 34046, + 37971, + 32077, + 34313, + 35821, + 32296, + 34691, + 33662, + 32578, + 35251, + 31501, + 32881, + 35861, + 29360, + 33194, + 36490, + 27253, + 33511, + 37098, + 25167, + 33818, + 37830, + 23203, + 34161, + 38559, + 21278, + 34487, + 39286, + 19617, + 34828, + 39961, + 18108, + 35266, + 40635, + 16378, + 30155, + 31861, + 50161, + 31176, + 31832, + 47943, + 32104, + 31810, + 45784, + 32924, + 31842, + 43617, + 33667, + 31898, + 41428, + 34199, + 32078, + 39276, + 34668, + 32284, + 37132, + 34866, + 32616, + 35008, + 35036, + 32896, + 32896, + 35375, + 33689, + 30745, + 35682, + 34370, + 28620, + 36016, + 35054, + 26582, + 36349, + 35668, + 24530, + 36775, + 36405, + 22560, + 37237, + 37097, + 20617, + 37395, + 37969, + 19091, + 37699, + 38893, + 17200, + 33060, + 29519, + 51402, + 34034, + 29620, + 49183, + 34871, + 29649, + 47034, + 35665, + 29686, + 44884, + 36303, + 29820, + 42739, + 36876, + 29981, + 40593, + 37256, + 30268, + 38483, + 37580, + 30593, + 36374, + 37749, + 31038, + 34279, + 37934, + 31535, + 32164, + 38219, + 32255, + 30009, + 38564, + 32970, + 27947, + 38913, + 33684, + 25961, + 39251, + 34403, + 23981, + 39642, + 35182, + 22065, + 40062, + 35920, + 20187, + 40381, + 36794, + 18386, + 36108, + 27186, + 52626, + 36980, + 27382, + 50340, + 37715, + 27529, + 48211, + 38385, + 27667, + 46111, + 38951, + 27828, + 44000, + 39421, + 28021, + 41898, + 39799, + 28292, + 39809, + 40118, + 28620, + 37716, + 40363, + 29081, + 35626, + 40541, + 29583, + 33554, + 40759, + 30216, + 31419, + 41047, + 30923, + 29285, + 41385, + 31653, + 27287, + 41793, + 32367, + 25327, + 42156, + 33180, + 23382, + 42462, + 34015, + 21452, + 42810, + 34895, + 19477, + 38941, + 24838, + 53718, + 39734, + 25089, + 51451, + 40490, + 25312, + 49335, + 41094, + 25596, + 47288, + 41637, + 25855, + 45217, + 41987, + 26124, + 43193, + 42335, + 26378, + 41168, + 42570, + 26740, + 39151, + 42802, + 27103, + 37112, + 42980, + 27649, + 34958, + 43178, + 28182, + 32839, + 43466, + 28938, + 30656, + 43744, + 29660, + 28556, + 44083, + 30423, + 26610, + 44490, + 31148, + 24656, + 44884, + 32043, + 22665, + 45229, + 32969, + 20589, + 41506, + 22894, + 54714, + 42277, + 23335, + 52224, + 42932, + 23591, + 50170, + 43487, + 23814, + 48220, + 43935, + 24058, + 46310, + 44331, + 24291, + 44378, + 44667, + 24539, + 42415, + 44964, + 24848, + 40450, + 45141, + 25243, + 38483, + 45310, + 25673, + 36462, + 45457, + 26265, + 34290, + 45644, + 26902, + 32127, + 45945, + 27695, + 29937, + 46340, + 28462, + 27890, + 46713, + 29244, + 25923, + 47100, + 30060, + 23904, + 47515, + 31048, + 21708, + 43769, + 21491, + 55031, + 44538, + 21905, + 52650, + 45146, + 22141, + 50770, + 45696, + 22345, + 48954, + 46056, + 22614, + 47196, + 46413, + 22854, + 45426, + 46691, + 23096, + 43610, + 46964, + 23342, + 41768, + 47182, + 23638, + 39879, + 47358, + 23979, + 37956, + 47533, + 24419, + 35878, + 47687, + 25003, + 33650, + 47902, + 25742, + 31425, + 48226, + 26536, + 29233, + 48645, + 27360, + 27185, + 49158, + 28147, + 25184, + 49600, + 29178, + 22852, + 45904, + 20325, + 55170, + 46607, + 20563, + 52949, + 47149, + 20824, + 51229, + 47666, + 21074, + 49546, + 48026, + 21324, + 47915, + 48340, + 21564, + 46285, + 48628, + 21789, + 44627, + 48860, + 22022, + 42903, + 49102, + 22254, + 41165, + 49273, + 22542, + 39313, + 49435, + 22853, + 37434, + 49574, + 23347, + 35296, + 49734, + 23897, + 33057, + 50066, + 24677, + 30698, + 50395, + 25514, + 28478, + 50782, + 26442, + 26334, + 51409, + 27641, + 23494, + 48133, + 19509, + 54966, + 48716, + 19739, + 53119, + 49169, + 19923, + 51570, + 49644, + 20061, + 50100, + 50030, + 20180, + 48626, + 50296, + 20314, + 47120, + 50563, + 20453, + 45601, + 50790, + 20640, + 44000, + 51000, + 20868, + 42344, + 51202, + 21110, + 40635, + 51350, + 21416, + 38753, + 51501, + 21736, + 36861, + 51620, + 22304, + 34698, + 51778, + 22889, + 32506, + 52086, + 23732, + 30086, + 52462, + 24647, + 27682, + 52885, + 25764, + 25115, + 50324, + 18600, + 55171, + 50883, + 18878, + 53477, + 51335, + 18990, + 52103, + 51751, + 19055, + 50774, + 52160, + 19078, + 49469, + 52404, + 19167, + 48042, + 52622, + 19276, + 46576, + 52829, + 19395, + 45096, + 53011, + 19564, + 43496, + 53192, + 19750, + 41877, + 53359, + 19979, + 40139, + 53462, + 20286, + 38220, + 53593, + 20677, + 36251, + 53749, + 21203, + 34188, + 53918, + 21855, + 31977, + 54183, + 22724, + 29509, + 54656, + 23769, + 26774, + 17245, + 47383, + 43593, + 17627, + 47576, + 41346, + 17778, + 47930, + 39191, + 17828, + 48342, + 37048, + 17835, + 48748, + 34894, + 17800, + 49150, + 32728, + 17755, + 49582, + 30698, + 17665, + 49974, + 28689, + 17647, + 50441, + 26666, + 17686, + 50823, + 24670, + 17547, + 51038, + 22424, + 17523, + 51182, + 20259, + 17548, + 51449, + 18669, + 18473, + 51188, + 17230, + 19122, + 51137, + 15981, + 19824, + 51122, + 15012, + 20465, + 51123, + 13984, + 18616, + 45890, + 44249, + 19201, + 45985, + 41947, + 19581, + 46216, + 39757, + 19791, + 46562, + 37602, + 19921, + 46962, + 35437, + 20013, + 47366, + 33257, + 20056, + 47868, + 31185, + 20064, + 48367, + 29133, + 20149, + 48869, + 27052, + 20227, + 49326, + 25042, + 20333, + 49550, + 22988, + 20444, + 49718, + 20940, + 20796, + 49753, + 19284, + 21174, + 49759, + 17668, + 21585, + 49773, + 16268, + 22149, + 49854, + 15306, + 22712, + 49925, + 14289, + 19897, + 44452, + 44983, + 20622, + 44476, + 42624, + 21233, + 44554, + 40351, + 21622, + 44827, + 38162, + 21911, + 45173, + 35975, + 22079, + 45593, + 33800, + 22192, + 46102, + 31712, + 22242, + 46675, + 29694, + 22339, + 47192, + 27628, + 22481, + 47680, + 25579, + 22630, + 48054, + 23529, + 22788, + 48291, + 21417, + 23086, + 48425, + 19679, + 23463, + 48499, + 18175, + 23811, + 48547, + 16668, + 24326, + 48679, + 15625, + 24878, + 48758, + 14558, + 21233, + 42954, + 45851, + 22045, + 42902, + 43412, + 22790, + 42871, + 41094, + 23369, + 43049, + 38910, + 23903, + 43261, + 36728, + 24116, + 43713, + 34537, + 24308, + 44196, + 32367, + 24432, + 44838, + 30330, + 24541, + 45426, + 28301, + 24742, + 45892, + 26204, + 24921, + 46340, + 24173, + 25124, + 46643, + 22128, + 25358, + 46894, + 20204, + 25727, + 47050, + 18718, + 26067, + 47164, + 17207, + 26463, + 47297, + 15954, + 26962, + 47466, + 14851, + 22692, + 41417, + 46760, + 23610, + 41287, + 44248, + 24413, + 41220, + 41913, + 25159, + 41256, + 39704, + 25832, + 41390, + 37563, + 26200, + 41742, + 35417, + 26438, + 42182, + 33260, + 26628, + 42721, + 31212, + 26766, + 43312, + 29193, + 26977, + 43853, + 27050, + 27203, + 44368, + 24918, + 27448, + 44843, + 22881, + 27660, + 45237, + 20844, + 28036, + 45518, + 19305, + 28402, + 45735, + 17829, + 28744, + 45915, + 16372, + 29232, + 46207, + 15163, + 24603, + 39691, + 47772, + 25555, + 39516, + 45274, + 26404, + 39415, + 43000, + 27224, + 39339, + 40763, + 27857, + 39481, + 38597, + 28378, + 39706, + 36431, + 28654, + 40165, + 34260, + 28901, + 40647, + 32146, + 29092, + 41177, + 30156, + 29277, + 41706, + 28111, + 29503, + 42214, + 25890, + 29766, + 42760, + 23843, + 30076, + 43303, + 21931, + 30385, + 43764, + 20136, + 30763, + 44148, + 18668, + 31138, + 44486, + 17189, + 31581, + 44914, + 15688, + 26551, + 37885, + 48792, + 27588, + 37709, + 46389, + 28550, + 37570, + 44124, + 29412, + 37474, + 41923, + 30092, + 37527, + 39727, + 30632, + 37703, + 37551, + 30992, + 38052, + 35387, + 31272, + 38474, + 33233, + 31527, + 38980, + 31180, + 31735, + 39521, + 29179, + 31939, + 40070, + 27044, + 32102, + 40573, + 24816, + 32454, + 41240, + 22890, + 32811, + 41838, + 21040, + 33172, + 42339, + 19510, + 33539, + 42795, + 18054, + 33943, + 43256, + 16425, + 28825, + 35943, + 49872, + 29918, + 35755, + 47556, + 30908, + 35620, + 45332, + 31758, + 35557, + 43130, + 32553, + 35520, + 40919, + 33048, + 35721, + 38763, + 33476, + 35958, + 36608, + 33678, + 36371, + 34441, + 33898, + 36834, + 32280, + 34176, + 37360, + 30231, + 34423, + 37912, + 28207, + 34687, + 38490, + 26141, + 34947, + 39071, + 24102, + 35290, + 39757, + 22223, + 35629, + 40398, + 20396, + 35974, + 41025, + 18895, + 36364, + 41635, + 17234, + 31390, + 33915, + 51062, + 32455, + 33788, + 48777, + 33344, + 33712, + 46601, + 34177, + 33658, + 44405, + 34902, + 33680, + 42196, + 35492, + 33785, + 40021, + 35933, + 33982, + 37883, + 36229, + 34269, + 35763, + 36414, + 34642, + 33653, + 36662, + 35192, + 31538, + 36918, + 35791, + 29439, + 37207, + 36378, + 27379, + 37521, + 36939, + 25317, + 37875, + 37619, + 23409, + 38249, + 38314, + 21572, + 38549, + 39024, + 19852, + 38873, + 39817, + 18043, + 34041, + 31801, + 52259, + 35041, + 31747, + 50029, + 35941, + 31695, + 47859, + 36789, + 31646, + 45675, + 37458, + 31700, + 43476, + 38030, + 31810, + 41295, + 38416, + 32038, + 39162, + 38759, + 32276, + 37034, + 38921, + 32614, + 34957, + 39073, + 32896, + 32896, + 39420, + 33652, + 30807, + 39730, + 34310, + 28740, + 40064, + 34982, + 26739, + 40379, + 35607, + 24714, + 40800, + 36329, + 22815, + 41248, + 37000, + 20943, + 41523, + 37831, + 19113, + 37033, + 29489, + 53370, + 37888, + 29531, + 51171, + 38691, + 29559, + 49031, + 39406, + 29587, + 46834, + 40060, + 29642, + 44643, + 40551, + 29811, + 42537, + 40979, + 30022, + 40442, + 41272, + 30328, + 38363, + 41521, + 30666, + 36288, + 41683, + 31084, + 34236, + 41880, + 31557, + 32168, + 42197, + 32244, + 30076, + 42554, + 32929, + 28047, + 42915, + 33630, + 26083, + 43243, + 34328, + 24115, + 43556, + 35137, + 22192, + 43941, + 35950, + 20188, + 39889, + 27134, + 54453, + 40686, + 27258, + 52238, + 41409, + 27404, + 50115, + 42040, + 27554, + 47985, + 42597, + 27692, + 45849, + 43038, + 27869, + 43775, + 43418, + 28068, + 41733, + 43711, + 28359, + 39672, + 43956, + 28704, + 37582, + 44166, + 29166, + 35529, + 44357, + 29636, + 33497, + 44610, + 30257, + 31421, + 44911, + 30954, + 29345, + 45274, + 31665, + 27358, + 45716, + 32354, + 25393, + 46088, + 33152, + 23404, + 46393, + 34040, + 21315, + 42401, + 24880, + 55452, + 43250, + 25289, + 52944, + 43927, + 25447, + 50913, + 44563, + 25556, + 48934, + 45015, + 25783, + 46945, + 45434, + 25981, + 44955, + 45761, + 26242, + 42985, + 46079, + 26489, + 41015, + 46265, + 26862, + 39015, + 46456, + 27226, + 36999, + 46647, + 27755, + 34886, + 46859, + 28261, + 32812, + 47200, + 28991, + 30707, + 47503, + 29687, + 28646, + 47888, + 30441, + 26660, + 48333, + 31163, + 24666, + 48796, + 32103, + 22488, + 44789, + 23408, + 55768, + 45612, + 23714, + 53382, + 46205, + 23833, + 51513, + 46769, + 23931, + 49670, + 47194, + 24099, + 47840, + 47564, + 24278, + 45999, + 47903, + 24480, + 44137, + 48214, + 24746, + 42255, + 48461, + 25071, + 40360, + 48607, + 25467, + 38433, + 48760, + 25886, + 36445, + 48928, + 26450, + 34282, + 49165, + 27050, + 32150, + 49556, + 27797, + 30016, + 50009, + 28553, + 27932, + 50372, + 29372, + 25853, + 50790, + 30317, + 23531, + 46978, + 22047, + 56002, + 47731, + 22306, + 53745, + 48285, + 22485, + 51962, + 48794, + 22643, + 50245, + 49228, + 22799, + 48557, + 49562, + 22963, + 46884, + 49882, + 23118, + 45192, + 50148, + 23343, + 43432, + 50411, + 23580, + 41646, + 50609, + 23880, + 39779, + 50773, + 24227, + 37864, + 50939, + 24679, + 35808, + 51090, + 25248, + 33651, + 51357, + 25956, + 31453, + 51737, + 26742, + 29247, + 52154, + 27616, + 27059, + 52703, + 28692, + 24369, + 49116, + 20751, + 55782, + 49823, + 20989, + 54003, + 50387, + 21189, + 52417, + 50857, + 21331, + 50885, + 51292, + 21436, + 49347, + 51587, + 21595, + 47778, + 51857, + 21757, + 46191, + 52104, + 21938, + 44560, + 52320, + 22183, + 42848, + 52534, + 22434, + 41115, + 52690, + 22766, + 39214, + 52845, + 23118, + 37290, + 53000, + 23619, + 35204, + 53169, + 24147, + 33075, + 53488, + 24949, + 30730, + 53865, + 25778, + 28440, + 54339, + 26795, + 25939, + 51617, + 19690, + 56090, + 52165, + 19931, + 54454, + 52655, + 20111, + 52948, + 53028, + 20168, + 51524, + 53420, + 20178, + 50117, + 53720, + 20251, + 48668, + 53964, + 20388, + 47156, + 54195, + 20541, + 45625, + 54404, + 20756, + 44011, + 54591, + 20996, + 42341, + 54775, + 21244, + 40624, + 54905, + 21586, + 38725, + 55049, + 21938, + 36820, + 55210, + 22457, + 34729, + 55390, + 23039, + 32570, + 55693, + 23901, + 30069, + 56139, + 24825, + 27468, + 19423, + 48787, + 44850, + 19665, + 49043, + 42676, + 19801, + 49355, + 40556, + 19741, + 49786, + 38353, + 19649, + 50202, + 36159, + 19658, + 50514, + 33996, + 19614, + 50856, + 31909, + 19559, + 51180, + 29935, + 19515, + 51494, + 27980, + 19498, + 51825, + 26055, + 19450, + 52096, + 24134, + 19354, + 52233, + 22124, + 19045, + 52475, + 20113, + 19195, + 52524, + 18521, + 19833, + 52285, + 16974, + 20756, + 52030, + 15788, + 21348, + 52047, + 14721, + 20700, + 47351, + 45495, + 21138, + 47483, + 43246, + 21494, + 47665, + 41071, + 21644, + 48020, + 38864, + 21731, + 48408, + 36652, + 21805, + 48792, + 34450, + 21841, + 49200, + 32285, + 21813, + 49644, + 30285, + 21806, + 50042, + 28310, + 21867, + 50414, + 26413, + 21904, + 50760, + 24560, + 21997, + 50846, + 22596, + 22084, + 50940, + 20646, + 22383, + 50892, + 19068, + 22687, + 50825, + 17496, + 23073, + 50802, + 16120, + 23612, + 50845, + 15040, + 21905, + 45987, + 46158, + 22499, + 46010, + 43806, + 23012, + 46096, + 41592, + 23376, + 46323, + 39390, + 23664, + 46608, + 37178, + 23824, + 46996, + 34986, + 23951, + 47396, + 32782, + 23962, + 47978, + 30770, + 23923, + 48557, + 28739, + 24033, + 49056, + 26794, + 24141, + 49460, + 24955, + 24277, + 49604, + 22968, + 24417, + 49691, + 20929, + 24688, + 49691, + 19364, + 24969, + 49659, + 17873, + 25248, + 49621, + 16396, + 25780, + 49660, + 15233, + 23170, + 44619, + 46948, + 23890, + 44535, + 44450, + 24505, + 44551, + 42222, + 25064, + 44635, + 40013, + 25521, + 44850, + 37796, + 25814, + 45186, + 35605, + 25999, + 45579, + 33442, + 26104, + 46103, + 31408, + 26146, + 46682, + 29427, + 26249, + 47200, + 27448, + 26374, + 47695, + 25530, + 26511, + 48044, + 23581, + 26677, + 48226, + 21541, + 26900, + 48310, + 19795, + 27175, + 48300, + 18306, + 27427, + 48284, + 16781, + 27877, + 48359, + 15530, + 24688, + 43072, + 47908, + 25498, + 42886, + 45320, + 26193, + 42833, + 43050, + 26874, + 42793, + 40812, + 27419, + 42977, + 38632, + 27878, + 43222, + 36453, + 28097, + 43650, + 34304, + 28269, + 44122, + 32193, + 28345, + 44726, + 30175, + 28436, + 45295, + 28162, + 28640, + 45761, + 26179, + 28805, + 46212, + 24261, + 28999, + 46505, + 22300, + 29194, + 46751, + 20386, + 29504, + 46867, + 18918, + 29811, + 46974, + 17440, + 30237, + 47137, + 15888, + 26327, + 41449, + 48815, + 27182, + 41257, + 46315, + 28000, + 41109, + 43959, + 28749, + 41015, + 41675, + 29358, + 41103, + 39493, + 29889, + 41279, + 37355, + 30231, + 41647, + 35236, + 30499, + 42060, + 33121, + 30654, + 42581, + 31131, + 30746, + 43142, + 29163, + 30893, + 43665, + 27096, + 31052, + 44173, + 25057, + 31296, + 44630, + 23141, + 31542, + 45017, + 21221, + 31863, + 45321, + 19636, + 32218, + 45578, + 18188, + 32607, + 45827, + 16571, + 28154, + 39742, + 49800, + 29178, + 39475, + 47367, + 30149, + 39251, + 45051, + 30920, + 39159, + 42806, + 31613, + 39128, + 40577, + 32099, + 39323, + 38427, + 32510, + 39583, + 36288, + 32781, + 39994, + 34166, + 33010, + 40458, + 32097, + 33150, + 41017, + 30136, + 33275, + 41548, + 28150, + 33421, + 42030, + 26028, + 33617, + 42545, + 24044, + 33933, + 43069, + 22225, + 34222, + 43517, + 20421, + 34585, + 43933, + 18935, + 34974, + 44336, + 17343, + 30398, + 37802, + 50863, + 31469, + 37521, + 48443, + 32385, + 37360, + 46199, + 33191, + 37252, + 43967, + 33905, + 37207, + 41727, + 34426, + 37355, + 39560, + 34844, + 37579, + 37431, + 35113, + 37949, + 35308, + 35332, + 38369, + 33194, + 35531, + 38886, + 31173, + 35683, + 39439, + 29202, + 35876, + 39955, + 27143, + 36065, + 40406, + 25026, + 36371, + 41010, + 23149, + 36692, + 41585, + 21397, + 37014, + 42117, + 19775, + 37405, + 42672, + 18090, + 32845, + 35820, + 51993, + 33870, + 35586, + 49643, + 34746, + 35445, + 47416, + 35579, + 35324, + 45187, + 36259, + 35341, + 42974, + 36872, + 35407, + 40776, + 37247, + 35627, + 38649, + 37570, + 35877, + 36529, + 37747, + 36287, + 34421, + 37936, + 36743, + 32317, + 38157, + 37275, + 30293, + 38359, + 37822, + 28311, + 38629, + 38375, + 26300, + 38895, + 38908, + 24302, + 39254, + 39555, + 22481, + 39595, + 40160, + 20654, + 39956, + 40841, + 18856, + 35257, + 33874, + 53144, + 36248, + 33720, + 50885, + 37181, + 33583, + 48697, + 38010, + 33496, + 46439, + 38723, + 33473, + 44205, + 39273, + 33565, + 42029, + 39697, + 33725, + 39889, + 40009, + 33939, + 37775, + 40233, + 34235, + 35700, + 40396, + 34602, + 33650, + 40642, + 35131, + 31597, + 40918, + 35719, + 29559, + 41192, + 36315, + 27536, + 41473, + 36906, + 25500, + 41799, + 37545, + 23616, + 42162, + 38218, + 21790, + 42521, + 38932, + 19871, + 37958, + 31819, + 54240, + 38928, + 31708, + 51984, + 39798, + 31607, + 49844, + 40578, + 31536, + 47622, + 41286, + 31491, + 45350, + 41755, + 31626, + 43232, + 42172, + 31775, + 41129, + 42435, + 32038, + 39039, + 42672, + 32301, + 36947, + 42838, + 32628, + 34914, + 43013, + 32896, + 32896, + 43335, + 33638, + 30854, + 43621, + 34291, + 28829, + 43962, + 34936, + 26856, + 44286, + 35527, + 24875, + 44609, + 36277, + 22953, + 44986, + 37078, + 20906, + 40949, + 29438, + 55292, + 41746, + 29495, + 53010, + 42464, + 29530, + 50892, + 43149, + 29553, + 48763, + 43733, + 29583, + 46576, + 44241, + 29649, + 44440, + 44611, + 29816, + 42391, + 44922, + 30033, + 40333, + 45125, + 30373, + 38260, + 45316, + 30733, + 36206, + 45508, + 31122, + 34192, + 45733, + 31577, + 32167, + 46039, + 32248, + 30114, + 46383, + 32912, + 28099, + 46787, + 33596, + 26137, + 47164, + 34278, + 24157, + 47517, + 35115, + 22072, + 43489, + 27301, + 56276, + 44336, + 27495, + 53844, + 45021, + 27583, + 51755, + 45640, + 27611, + 49711, + 46139, + 27686, + 47650, + 46582, + 27774, + 45590, + 46929, + 27948, + 43581, + 47235, + 28144, + 41586, + 47467, + 28439, + 39561, + 47670, + 28773, + 37519, + 47861, + 29232, + 35500, + 48052, + 29691, + 33495, + 48324, + 30288, + 31457, + 48635, + 30952, + 29411, + 49030, + 31647, + 27395, + 49493, + 32364, + 25377, + 49946, + 33204, + 23249, + 45985, + 25434, + 56587, + 46734, + 25630, + 54380, + 47361, + 25714, + 52377, + 47920, + 25760, + 50454, + 48411, + 25839, + 48542, + 48785, + 25992, + 46631, + 49136, + 26151, + 44724, + 49414, + 26406, + 42822, + 49675, + 26654, + 40905, + 49836, + 27021, + 38944, + 50006, + 27370, + 36967, + 50211, + 27874, + 34877, + 50436, + 28353, + 32821, + 50814, + 29070, + 30725, + 51137, + 29759, + 28662, + 51484, + 30577, + 26569, + 51932, + 31477, + 24249, + 48324, + 23942, + 56816, + 48978, + 24044, + 54752, + 49557, + 24086, + 52883, + 50067, + 24143, + 51123, + 50543, + 24185, + 49369, + 50880, + 24312, + 47600, + 51187, + 24448, + 45812, + 51463, + 24665, + 43997, + 51707, + 24970, + 42162, + 51909, + 25305, + 40287, + 52058, + 25701, + 38350, + 52216, + 26119, + 36364, + 52401, + 26639, + 34268, + 52660, + 27210, + 32157, + 53098, + 27969, + 29969, + 53551, + 28768, + 27772, + 53929, + 29826, + 25100, + 50620, + 22460, + 56844, + 51230, + 22607, + 55088, + 51784, + 22759, + 53402, + 52219, + 22833, + 51762, + 52619, + 22879, + 50129, + 52951, + 22959, + 48488, + 53204, + 23101, + 46816, + 53456, + 23244, + 45130, + 53676, + 23499, + 43369, + 53889, + 23760, + 41585, + 54071, + 24080, + 39703, + 54247, + 24433, + 37777, + 54415, + 24910, + 35748, + 54587, + 25469, + 33645, + 54871, + 26155, + 31427, + 55281, + 26943, + 29148, + 55788, + 27876, + 26703, + 53110, + 20943, + 56896, + 53650, + 21141, + 55402, + 54190, + 21254, + 53950, + 54596, + 21348, + 52456, + 54946, + 21423, + 50946, + 55214, + 21568, + 49400, + 55470, + 21719, + 47825, + 55658, + 21905, + 46203, + 55833, + 22102, + 44541, + 56021, + 22315, + 42837, + 56198, + 22534, + 41112, + 56367, + 22849, + 39245, + 56550, + 23170, + 37367, + 56689, + 23713, + 35254, + 56916, + 24318, + 33085, + 57159, + 25106, + 30683, + 57559, + 25954, + 28203, + 21319, + 50119, + 45996, + 21570, + 50380, + 43854, + 21642, + 50752, + 41765, + 21619, + 51139, + 39600, + 21524, + 51515, + 37385, + 21476, + 51841, + 35214, + 21422, + 52153, + 33049, + 21386, + 52436, + 31083, + 21345, + 52685, + 29143, + 21281, + 52973, + 27304, + 21243, + 53220, + 25511, + 21196, + 53355, + 23726, + 21018, + 53469, + 21906, + 21163, + 53435, + 20151, + 21566, + 53204, + 18517, + 21688, + 53100, + 16843, + 22264, + 52990, + 15554, + 22685, + 48734, + 46620, + 23166, + 48819, + 44372, + 23395, + 49092, + 42245, + 23518, + 49415, + 40084, + 23552, + 49771, + 37845, + 23581, + 50120, + 35639, + 23606, + 50458, + 33452, + 23582, + 50854, + 31427, + 23544, + 51237, + 29470, + 23557, + 51583, + 27624, + 23581, + 51911, + 25869, + 23598, + 52165, + 24111, + 23652, + 52162, + 22196, + 23717, + 52149, + 20343, + 23971, + 51996, + 18825, + 24229, + 51851, + 17298, + 24595, + 51776, + 15914, + 23891, + 47382, + 47320, + 24500, + 47374, + 44934, + 24868, + 47550, + 42776, + 25202, + 47749, + 40610, + 25426, + 48040, + 38365, + 25587, + 48373, + 36136, + 25676, + 48764, + 33914, + 25696, + 49239, + 31820, + 25664, + 49722, + 29886, + 25677, + 50146, + 28011, + 25748, + 50522, + 26255, + 25787, + 50891, + 24548, + 25870, + 50897, + 22578, + 25947, + 50931, + 20609, + 26175, + 50802, + 19103, + 26407, + 50678, + 17581, + 26705, + 50581, + 16097, + 25115, + 46091, + 48171, + 25795, + 45989, + 45602, + 26334, + 46019, + 43358, + 26841, + 46079, + 41186, + 27228, + 46297, + 38968, + 27564, + 46550, + 36750, + 27702, + 46940, + 34581, + 27802, + 47363, + 32435, + 27811, + 47939, + 30481, + 27795, + 48512, + 28541, + 27879, + 49015, + 26717, + 27954, + 49433, + 24984, + 28065, + 49577, + 23059, + 28183, + 49659, + 21042, + 28363, + 49569, + 19422, + 28556, + 49436, + 17888, + 28757, + 49316, + 16345, + 26337, + 44823, + 49347, + 27201, + 44567, + 46461, + 27897, + 44435, + 44075, + 28519, + 44410, + 41890, + 29052, + 44522, + 39689, + 29505, + 44743, + 37462, + 29749, + 45099, + 35313, + 29916, + 45476, + 33205, + 29984, + 45995, + 31224, + 30006, + 46548, + 29293, + 30094, + 47050, + 27399, + 30182, + 47543, + 25571, + 30284, + 47903, + 23714, + 30432, + 48077, + 21752, + 30623, + 48169, + 19986, + 30909, + 48187, + 18500, + 31252, + 48218, + 16798, + 28022, + 43221, + 50060, + 28908, + 42935, + 47428, + 29762, + 42694, + 44986, + 30425, + 42638, + 42754, + 31025, + 42633, + 40538, + 31491, + 42831, + 38367, + 31872, + 43096, + 36216, + 32089, + 43496, + 34122, + 32234, + 43952, + 32083, + 32260, + 44526, + 30137, + 32282, + 45090, + 28184, + 32421, + 45555, + 26275, + 32526, + 46022, + 24424, + 32748, + 46298, + 22575, + 32942, + 46542, + 20702, + 33263, + 46738, + 19187, + 33633, + 46946, + 17523, + 29872, + 41507, + 50920, + 30909, + 41162, + 48297, + 31740, + 40962, + 45969, + 32451, + 40847, + 43690, + 33080, + 40784, + 41431, + 33549, + 40937, + 39281, + 33972, + 41138, + 37169, + 34257, + 41520, + 35091, + 34485, + 41925, + 33012, + 34587, + 42458, + 31076, + 34638, + 43013, + 29162, + 34737, + 43512, + 27174, + 34838, + 43994, + 25204, + 35049, + 44432, + 23351, + 35292, + 44793, + 21512, + 35587, + 45128, + 19825, + 35977, + 45470, + 18230, + 32077, + 39607, + 51916, + 33210, + 39222, + 49360, + 33994, + 39063, + 47103, + 34740, + 38929, + 44851, + 35372, + 38910, + 42623, + 35921, + 38968, + 40427, + 36282, + 39195, + 38300, + 36571, + 39494, + 36181, + 36756, + 39929, + 34085, + 36904, + 40411, + 32045, + 36985, + 40973, + 30117, + 37091, + 41487, + 28192, + 37272, + 41901, + 26151, + 37460, + 42355, + 24214, + 37762, + 42856, + 22460, + 38052, + 43295, + 20685, + 38427, + 43768, + 18989, + 34260, + 37727, + 53060, + 35301, + 37404, + 50561, + 36189, + 37160, + 48258, + 36959, + 37008, + 45976, + 37636, + 37003, + 43758, + 38220, + 37073, + 41574, + 38613, + 37257, + 39446, + 38919, + 37478, + 37330, + 39127, + 37858, + 35252, + 39299, + 38277, + 33194, + 39454, + 38796, + 31221, + 39552, + 39353, + 29305, + 39728, + 39856, + 27314, + 39950, + 40279, + 25251, + 40253, + 40816, + 23362, + 40601, + 41378, + 21597, + 40985, + 41946, + 19757, + 36610, + 35817, + 54079, + 37669, + 35562, + 51726, + 38610, + 35351, + 49484, + 39405, + 35238, + 47234, + 40148, + 35145, + 44987, + 40654, + 35236, + 42824, + 41095, + 35349, + 40672, + 41342, + 35571, + 38559, + 41555, + 35829, + 36459, + 41698, + 36225, + 34415, + 41854, + 36665, + 32371, + 42073, + 37203, + 30381, + 42248, + 37744, + 28442, + 42504, + 38295, + 26468, + 42753, + 38802, + 24489, + 43104, + 39447, + 22645, + 43466, + 40076, + 20669, + 39335, + 33853, + 55130, + 40286, + 33721, + 52768, + 41114, + 33568, + 50622, + 41876, + 33448, + 48447, + 42563, + 33367, + 46190, + 43088, + 33385, + 44013, + 43458, + 33501, + 41889, + 43731, + 33694, + 39786, + 43934, + 33950, + 37698, + 44112, + 34255, + 35658, + 44269, + 34602, + 33649, + 44485, + 35122, + 31635, + 44728, + 35714, + 29628, + 45001, + 36280, + 27644, + 45316, + 36814, + 25660, + 45631, + 37447, + 23744, + 45999, + 38213, + 21678, + 42124, + 31787, + 56183, + 42915, + 31771, + 53877, + 43678, + 31689, + 51712, + 44409, + 31599, + 49573, + 45033, + 31537, + 47389, + 45582, + 31483, + 45206, + 45891, + 31613, + 43122, + 46169, + 31750, + 41046, + 46333, + 32053, + 38968, + 46475, + 32351, + 36885, + 46674, + 32653, + 34884, + 46870, + 32896, + 32896, + 47161, + 33613, + 30883, + 47429, + 34240, + 28881, + 47824, + 34876, + 26908, + 48229, + 35471, + 24921, + 48624, + 36214, + 22839, + 44756, + 29665, + 57138, + 45538, + 29684, + 54825, + 46233, + 29673, + 52662, + 46863, + 29630, + 50537, + 47424, + 29599, + 48422, + 47869, + 29619, + 46320, + 48237, + 29703, + 44264, + 48505, + 29873, + 42254, + 48729, + 30099, + 40238, + 48884, + 30427, + 38206, + 49041, + 30779, + 36179, + 49240, + 31157, + 34187, + 49470, + 31589, + 32183, + 49774, + 32223, + 30147, + 50113, + 32864, + 28126, + 50522, + 33613, + 26127, + 50945, + 34397, + 23968, + 47227, + 27694, + 57728, + 47965, + 27741, + 55461, + 48615, + 27760, + 53369, + 49188, + 27745, + 51328, + 49723, + 27724, + 49326, + 50090, + 27796, + 47355, + 50420, + 27870, + 45385, + 50680, + 28053, + 43431, + 50919, + 28246, + 41474, + 51111, + 28541, + 39485, + 51282, + 28876, + 37492, + 51464, + 29318, + 35494, + 51653, + 29754, + 33497, + 51937, + 30341, + 31458, + 52268, + 30988, + 29416, + 52624, + 31732, + 27341, + 53072, + 32610, + 25083, + 49692, + 25933, + 57985, + 50339, + 25949, + 55860, + 50910, + 25947, + 53936, + 51418, + 25958, + 52054, + 51875, + 25966, + 50191, + 52245, + 26029, + 48336, + 52519, + 26159, + 46474, + 52771, + 26318, + 44607, + 52992, + 26576, + 42734, + 53204, + 26825, + 40845, + 53363, + 27188, + 38884, + 53524, + 27533, + 36916, + 53737, + 27996, + 34860, + 53960, + 28427, + 32816, + 54364, + 29174, + 30668, + 54731, + 29897, + 28563, + 55140, + 30825, + 26115, + 52156, + 24114, + 57975, + 52683, + 24186, + 56158, + 53189, + 24257, + 54427, + 53688, + 24277, + 52732, + 54069, + 24286, + 51004, + 54407, + 24313, + 49267, + 54653, + 24441, + 47511, + 54883, + 24575, + 45736, + 55083, + 24838, + 43934, + 55267, + 25141, + 42106, + 55439, + 25467, + 40238, + 55599, + 25842, + 38318, + 55759, + 26250, + 36349, + 55936, + 26766, + 34256, + 56182, + 27324, + 32113, + 56645, + 28122, + 29833, + 57150, + 28998, + 27461, + 54755, + 22353, + 58104, + 55305, + 22401, + 56507, + 55804, + 22481, + 54978, + 56271, + 22559, + 53450, + 56579, + 22680, + 51856, + 56777, + 22863, + 50209, + 56954, + 23041, + 48535, + 57148, + 23196, + 46852, + 57300, + 23367, + 45139, + 57446, + 23595, + 43375, + 57594, + 23831, + 41591, + 57756, + 24137, + 39725, + 57940, + 24466, + 37822, + 58108, + 24954, + 35792, + 58297, + 25504, + 33672, + 58551, + 26202, + 31403, + 58964, + 27063, + 28961, + 23180, + 51255, + 47003, + 23490, + 51518, + 44913, + 23437, + 52050, + 42827, + 23420, + 52544, + 40791, + 23195, + 53011, + 38530, + 23171, + 53266, + 36350, + 23004, + 53573, + 34205, + 22995, + 53750, + 32159, + 23020, + 53901, + 30267, + 23024, + 54052, + 28412, + 22940, + 54258, + 26730, + 22846, + 54449, + 25052, + 22635, + 54550, + 23292, + 22758, + 54460, + 21538, + 22593, + 54480, + 19800, + 23206, + 54116, + 18251, + 23315, + 53985, + 16591, + 24591, + 49920, + 47645, + 25045, + 50048, + 45449, + 25231, + 50350, + 43339, + 25326, + 50732, + 41257, + 25383, + 51053, + 39046, + 25421, + 51356, + 36813, + 25430, + 51684, + 34650, + 25417, + 52017, + 32523, + 25362, + 52392, + 30606, + 25309, + 52732, + 28685, + 25301, + 53070, + 27030, + 25268, + 53340, + 25404, + 25243, + 53436, + 23683, + 25231, + 53395, + 21811, + 25280, + 53314, + 20048, + 25480, + 53080, + 18539, + 25691, + 52848, + 16976, + 25767, + 48767, + 48552, + 26386, + 48763, + 46038, + 26783, + 48888, + 43847, + 27042, + 49142, + 41696, + 27230, + 49403, + 39508, + 27382, + 49672, + 37291, + 27462, + 49996, + 35101, + 27511, + 50331, + 32919, + 27485, + 50768, + 31032, + 27430, + 51180, + 29155, + 27423, + 51571, + 27432, + 27403, + 51964, + 25784, + 27361, + 52284, + 24107, + 27378, + 52213, + 22171, + 27413, + 52160, + 20300, + 27627, + 51918, + 18798, + 27837, + 51690, + 17220, + 26979, + 47519, + 49481, + 27679, + 47423, + 46749, + 28223, + 47381, + 44450, + 28625, + 47522, + 42276, + 28989, + 47705, + 40095, + 29264, + 47968, + 37879, + 29424, + 48300, + 35684, + 29490, + 48683, + 33471, + 29498, + 49183, + 31485, + 29504, + 49629, + 29654, + 29517, + 50044, + 27886, + 29538, + 50444, + 26210, + 29520, + 50857, + 24577, + 29571, + 50849, + 22635, + 29613, + 50863, + 20678, + 29810, + 50692, + 19142, + 30037, + 50531, + 17507, + 28233, + 46293, + 50554, + 29077, + 46060, + 47575, + 29756, + 45878, + 45131, + 30293, + 45910, + 42963, + 30792, + 45959, + 40804, + 31168, + 46184, + 38584, + 31467, + 46446, + 36396, + 31592, + 46817, + 34285, + 31668, + 47239, + 32227, + 31657, + 47774, + 30350, + 31632, + 48304, + 28494, + 31650, + 48820, + 26723, + 31650, + 49288, + 25021, + 31723, + 49427, + 23169, + 31811, + 49475, + 21239, + 32016, + 49465, + 19587, + 32360, + 49435, + 17805, + 29800, + 44914, + 51366, + 30804, + 44501, + 48371, + 31508, + 44321, + 45996, + 32112, + 44249, + 43767, + 32656, + 44224, + 41586, + 33106, + 44373, + 39392, + 33498, + 44590, + 37184, + 33671, + 44965, + 35089, + 33804, + 45338, + 33018, + 33837, + 45848, + 31124, + 33836, + 46368, + 29268, + 33874, + 46855, + 27431, + 33900, + 47338, + 25638, + 33959, + 47708, + 23838, + 34104, + 47863, + 21960, + 34281, + 48008, + 20161, + 34628, + 48175, + 18469, + 31776, + 43090, + 52211, + 32907, + 42663, + 49179, + 33562, + 42550, + 46952, + 34198, + 42452, + 44728, + 34751, + 42429, + 42520, + 35237, + 42484, + 40334, + 35584, + 42702, + 38184, + 35844, + 42999, + 36059, + 35989, + 43410, + 33980, + 36066, + 43883, + 31978, + 36063, + 44447, + 30091, + 36053, + 44997, + 28202, + 36150, + 45428, + 26341, + 36214, + 45860, + 24526, + 36427, + 46108, + 22710, + 36617, + 46333, + 20879, + 36959, + 46644, + 19174, + 33735, + 41379, + 53024, + 34794, + 41015, + 50366, + 35647, + 40772, + 48031, + 36335, + 40632, + 45764, + 36928, + 40587, + 43535, + 37451, + 40605, + 41328, + 37761, + 40811, + 39175, + 38019, + 41045, + 37025, + 38208, + 41448, + 34981, + 38366, + 41855, + 32936, + 38418, + 42392, + 31053, + 38429, + 42946, + 29188, + 38523, + 43402, + 27256, + 38629, + 43823, + 25331, + 38842, + 44237, + 23521, + 39135, + 44604, + 21760, + 39465, + 44975, + 19911, + 35815, + 39584, + 54049, + 36914, + 39199, + 51507, + 37899, + 38906, + 49183, + 38593, + 38795, + 46923, + 39232, + 38718, + 44701, + 39731, + 38778, + 42515, + 40134, + 38888, + 40341, + 40363, + 39107, + 38188, + 40554, + 39404, + 36078, + 40699, + 39821, + 34054, + 40804, + 40291, + 32085, + 40811, + 40863, + 30227, + 40849, + 41398, + 28366, + 41076, + 41765, + 26337, + 41284, + 42160, + 24384, + 41624, + 42672, + 22636, + 41976, + 43172, + 20711, + 38279, + 37658, + 55004, + 39316, + 37363, + 52507, + 40135, + 37144, + 50264, + 40862, + 37001, + 48055, + 41563, + 36900, + 45859, + 42080, + 36917, + 43676, + 42485, + 36999, + 41492, + 42723, + 37205, + 39366, + 42906, + 37445, + 37253, + 43045, + 37823, + 35225, + 43158, + 38224, + 33214, + 43289, + 38723, + 31270, + 43374, + 39251, + 29382, + 43538, + 39743, + 27441, + 43762, + 40184, + 25444, + 44053, + 40704, + 23521, + 44431, + 41298, + 21521, + 41024, + 35680, + 56021, + 41925, + 35515, + 53562, + 42685, + 35358, + 51401, + 43419, + 35208, + 49264, + 44043, + 35123, + 47063, + 44595, + 35061, + 44875, + 44897, + 35164, + 42717, + 45150, + 35302, + 40568, + 45292, + 35574, + 38478, + 45421, + 35864, + 36405, + 45531, + 36234, + 34405, + 45642, + 36649, + 32404, + 45864, + 37171, + 30436, + 46048, + 37678, + 28513, + 46343, + 38202, + 26571, + 46618, + 38695, + 24609, + 46994, + 39427, + 22404, + 43620, + 33858, + 57116, + 44417, + 33754, + 54765, + 45159, + 33634, + 52543, + 45843, + 33515, + 50386, + 46449, + 33410, + 48213, + 46951, + 33349, + 46031, + 47297, + 33387, + 43905, + 47534, + 33494, + 41815, + 47698, + 33703, + 39731, + 47810, + 33979, + 37648, + 47954, + 34274, + 35628, + 48095, + 34588, + 33646, + 48299, + 35068, + 31657, + 48556, + 35625, + 29674, + 48890, + 36177, + 27695, + 49300, + 36740, + 25703, + 49642, + 37411, + 23626, + 46080, + 32015, + 58147, + 46866, + 31908, + 55776, + 47578, + 31817, + 53577, + 48236, + 31682, + 51402, + 48858, + 31531, + 49234, + 49278, + 31522, + 47116, + 49649, + 31528, + 45020, + 49856, + 31662, + 43000, + 50051, + 31790, + 40978, + 50149, + 32092, + 38917, + 50229, + 32383, + 36843, + 50429, + 32668, + 34866, + 50620, + 32896, + 32896, + 50901, + 33570, + 30917, + 51165, + 34164, + 28948, + 51541, + 34873, + 26944, + 51969, + 35646, + 24682, + 48598, + 29933, + 58855, + 49347, + 29864, + 56507, + 49983, + 29814, + 54382, + 50567, + 29749, + 52283, + 51089, + 29678, + 50200, + 51497, + 29661, + 48153, + 51794, + 29688, + 46132, + 52043, + 29784, + 44137, + 52245, + 29954, + 42167, + 52413, + 30187, + 40185, + 52534, + 30512, + 38179, + 52673, + 30850, + 36172, + 52861, + 31201, + 34185, + 53088, + 31613, + 32186, + 53429, + 32214, + 30168, + 53789, + 32847, + 28139, + 54181, + 33754, + 25895, + 51021, + 27971, + 59304, + 51733, + 27928, + 56966, + 52298, + 27911, + 54977, + 52830, + 27898, + 53026, + 53281, + 27876, + 51060, + 53689, + 27852, + 49115, + 53914, + 27919, + 47188, + 54132, + 27982, + 45257, + 54339, + 28167, + 43340, + 54539, + 28352, + 41415, + 54704, + 28645, + 39457, + 54846, + 28987, + 37507, + 55017, + 29390, + 35514, + 55208, + 29785, + 33500, + 55534, + 30391, + 31417, + 55930, + 31073, + 29329, + 56354, + 31842, + 27106, + 53535, + 26036, + 59367, + 54162, + 26033, + 57273, + 54682, + 26031, + 55493, + 55192, + 26017, + 53732, + 55557, + 26028, + 51915, + 55845, + 26060, + 50070, + 56078, + 26147, + 48232, + 56271, + 26276, + 46398, + 56452, + 26438, + 44554, + 56618, + 26694, + 42686, + 56781, + 26943, + 40803, + 56932, + 27287, + 38879, + 57077, + 27618, + 36952, + 57247, + 28051, + 34877, + 57428, + 28433, + 32802, + 57939, + 29273, + 30583, + 58459, + 30034, + 28385, + 56264, + 24018, + 59478, + 56903, + 23995, + 57682, + 57570, + 23878, + 56072, + 57965, + 23933, + 54461, + 58387, + 23951, + 52853, + 58416, + 24175, + 51083, + 58449, + 24372, + 49289, + 58607, + 24530, + 47546, + 58762, + 24699, + 45794, + 58899, + 24964, + 44005, + 59030, + 25263, + 42184, + 59159, + 25577, + 40315, + 59301, + 25924, + 38391, + 59464, + 26290, + 36421, + 59697, + 26756, + 34311, + 59970, + 27337, + 32122, + 60422, + 28186, + 29736, + 25250, + 52220, + 47871, + 25439, + 52546, + 45871, + 25213, + 53259, + 43798, + 25193, + 53735, + 41814, + 25194, + 54051, + 39744, + 25143, + 54292, + 37556, + 25070, + 54509, + 35443, + 25035, + 54661, + 33369, + 25026, + 54801, + 31461, + 24948, + 54972, + 29602, + 24878, + 55132, + 27882, + 24780, + 55292, + 26280, + 24547, + 55502, + 24656, + 24437, + 55463, + 22930, + 24557, + 55324, + 21210, + 24617, + 55202, + 19553, + 24796, + 54994, + 17988, + 26563, + 51016, + 48491, + 26921, + 51182, + 46431, + 27190, + 51410, + 44412, + 27300, + 51770, + 42339, + 27373, + 52131, + 40234, + 27418, + 52425, + 37996, + 27420, + 52748, + 35808, + 27385, + 53091, + 33674, + 27291, + 53409, + 31725, + 27167, + 53673, + 29903, + 27061, + 53920, + 28169, + 26979, + 54159, + 26599, + 26888, + 54375, + 25029, + 26818, + 54394, + 23300, + 26761, + 54338, + 21487, + 26795, + 54225, + 19782, + 26949, + 54019, + 18209, + 27770, + 49971, + 49142, + 28233, + 50015, + 47005, + 28676, + 50051, + 44921, + 28925, + 50287, + 42798, + 29138, + 50540, + 40687, + 29283, + 50802, + 38479, + 29382, + 51095, + 36285, + 29400, + 51435, + 34133, + 29375, + 51806, + 32111, + 29285, + 52201, + 30289, + 29176, + 52589, + 28485, + 29095, + 53010, + 26896, + 28993, + 53356, + 25347, + 28908, + 53472, + 23655, + 28845, + 53413, + 21768, + 28847, + 53345, + 19957, + 29055, + 52998, + 18437, + 28954, + 48961, + 49925, + 29565, + 48844, + 47628, + 30145, + 48722, + 45471, + 30517, + 48867, + 43317, + 30825, + 49084, + 41178, + 31044, + 49317, + 38998, + 31235, + 49554, + 36821, + 31302, + 49873, + 34674, + 31343, + 50212, + 32571, + 31308, + 50628, + 30783, + 31239, + 51031, + 28991, + 31188, + 51431, + 27337, + 31116, + 51833, + 25733, + 31031, + 52137, + 24091, + 31022, + 52033, + 22208, + 31028, + 51956, + 20358, + 31230, + 51757, + 18778, + 30414, + 47527, + 51138, + 31145, + 47350, + 48364, + 31716, + 47255, + 46168, + 32194, + 47271, + 44011, + 32590, + 47392, + 41867, + 32918, + 47590, + 39691, + 33176, + 47841, + 37493, + 33293, + 48178, + 35356, + 33348, + 48544, + 33212, + 33318, + 49031, + 31330, + 33293, + 49463, + 29557, + 33269, + 49870, + 27843, + 33235, + 50269, + 26194, + 33172, + 50669, + 24581, + 33207, + 50639, + 22719, + 33242, + 50628, + 20855, + 33485, + 50568, + 19012, + 32034, + 46086, + 52226, + 32881, + 45828, + 49090, + 33442, + 45773, + 46946, + 33984, + 45732, + 44805, + 34450, + 45774, + 42659, + 34867, + 45860, + 40504, + 35162, + 46080, + 38305, + 35363, + 46355, + 36153, + 35444, + 46713, + 34071, + 35476, + 47128, + 32085, + 35438, + 47644, + 30271, + 35384, + 48150, + 28477, + 35355, + 48622, + 26737, + 35293, + 49102, + 25043, + 35349, + 49257, + 23228, + 35440, + 49316, + 21352, + 35653, + 49409, + 19562, + 33838, + 44635, + 53020, + 34721, + 44311, + 50193, + 35395, + 44142, + 47900, + 35975, + 44068, + 45739, + 36484, + 44054, + 43574, + 36945, + 44076, + 41402, + 37238, + 44268, + 39225, + 37467, + 44496, + 37031, + 37549, + 44893, + 34941, + 37616, + 45275, + 32867, + 37614, + 45785, + 31043, + 37583, + 46295, + 29242, + 37595, + 46737, + 27453, + 37602, + 47157, + 25696, + 37649, + 47499, + 23932, + 37816, + 47668, + 22106, + 38007, + 47857, + 20229, + 35724, + 43033, + 53836, + 36646, + 42662, + 51288, + 37512, + 42381, + 48961, + 38106, + 42309, + 46762, + 38644, + 42260, + 44578, + 39088, + 42302, + 42391, + 39435, + 42412, + 40210, + 39623, + 42633, + 38028, + 39773, + 42940, + 35916, + 39884, + 43335, + 33879, + 39920, + 43804, + 31926, + 39868, + 44371, + 30083, + 39824, + 44904, + 28235, + 39906, + 45276, + 26408, + 39966, + 45646, + 24623, + 40256, + 45930, + 22894, + 40558, + 46217, + 21028, + 37731, + 41321, + 54842, + 38783, + 40925, + 52302, + 39660, + 40678, + 50047, + 40306, + 40540, + 47836, + 40837, + 40452, + 45639, + 41279, + 40485, + 43451, + 41659, + 40555, + 41252, + 41849, + 40755, + 39072, + 42007, + 40972, + 36896, + 42130, + 41377, + 34919, + 42229, + 41767, + 32921, + 42242, + 42294, + 31084, + 42222, + 42839, + 29268, + 42310, + 43273, + 27376, + 42429, + 43649, + 25472, + 42641, + 44057, + 23657, + 42953, + 44491, + 21764, + 40380, + 39367, + 55878, + 41330, + 39045, + 53246, + 42046, + 38889, + 51107, + 42715, + 38742, + 48971, + 43249, + 38672, + 46795, + 43711, + 38639, + 44619, + 44012, + 38718, + 42408, + 44242, + 38857, + 40232, + 44387, + 39100, + 38128, + 44497, + 39404, + 36067, + 44543, + 39796, + 34063, + 44579, + 40230, + 32104, + 44595, + 40733, + 30252, + 44624, + 41225, + 28416, + 44858, + 41640, + 26462, + 45048, + 42050, + 24545, + 45402, + 42602, + 22576, + 42941, + 37498, + 56843, + 43729, + 37279, + 54448, + 44432, + 37091, + 52211, + 45098, + 36929, + 50078, + 45648, + 36850, + 47925, + 46107, + 36808, + 45759, + 46406, + 36868, + 43582, + 46633, + 36977, + 41398, + 46758, + 37219, + 39297, + 46856, + 37482, + 37208, + 46925, + 37837, + 35213, + 46970, + 38210, + 33234, + 47092, + 38677, + 31311, + 47176, + 39159, + 29432, + 47352, + 39636, + 27508, + 47608, + 40089, + 25530, + 47909, + 40619, + 23460, + 45470, + 35685, + 57994, + 46232, + 35531, + 55615, + 46918, + 35392, + 53367, + 47539, + 35273, + 51211, + 48115, + 35158, + 49055, + 48521, + 35116, + 46903, + 48875, + 35100, + 44763, + 49054, + 35206, + 42648, + 49201, + 35346, + 40539, + 49265, + 35612, + 38453, + 49329, + 35887, + 36392, + 49386, + 36209, + 34408, + 49452, + 36570, + 32423, + 49697, + 37068, + 30482, + 49888, + 37561, + 28580, + 50217, + 38147, + 26632, + 50535, + 38715, + 24605, + 47855, + 33923, + 59049, + 48611, + 33825, + 56652, + 49257, + 33722, + 54472, + 49834, + 33602, + 52304, + 50363, + 33460, + 50133, + 50781, + 33388, + 47992, + 51111, + 33364, + 45881, + 51319, + 33426, + 43813, + 51455, + 33541, + 41765, + 51550, + 33752, + 39705, + 51614, + 34017, + 37635, + 51728, + 34291, + 35634, + 51851, + 34576, + 33672, + 52043, + 35018, + 31714, + 52290, + 35551, + 29770, + 52586, + 36137, + 27801, + 52992, + 36833, + 25698, + 50136, + 32105, + 59844, + 50831, + 32023, + 57563, + 51454, + 31906, + 55399, + 52057, + 31780, + 53250, + 52561, + 31665, + 51113, + 53021, + 31573, + 48996, + 53290, + 31559, + 46955, + 53525, + 31562, + 44922, + 53653, + 31715, + 42926, + 53772, + 31863, + 40926, + 53858, + 32146, + 38902, + 53935, + 32410, + 36869, + 54123, + 32679, + 34884, + 54318, + 32896, + 32896, + 54619, + 33572, + 30926, + 54893, + 34174, + 28951, + 55243, + 34941, + 26826, + 52522, + 30110, + 60444, + 53223, + 30015, + 58086, + 53757, + 29944, + 56033, + 54259, + 29876, + 54016, + 54694, + 29815, + 51995, + 55072, + 29760, + 49968, + 55344, + 29752, + 47978, + 55550, + 29778, + 46008, + 55736, + 29885, + 44056, + 55897, + 30060, + 42116, + 56039, + 30288, + 40172, + 56160, + 30584, + 38224, + 56302, + 30883, + 36250, + 56505, + 31210, + 34219, + 56777, + 31627, + 32162, + 57192, + 32268, + 30091, + 57638, + 32940, + 27972, + 54891, + 28151, + 60659, + 55492, + 28085, + 58533, + 56020, + 28036, + 56594, + 56499, + 27996, + 54715, + 56934, + 27955, + 52841, + 57216, + 27935, + 50896, + 57471, + 27933, + 48967, + 57651, + 28009, + 47091, + 57831, + 28080, + 45216, + 57995, + 28279, + 43322, + 58159, + 28472, + 41415, + 58308, + 28765, + 39488, + 58446, + 29097, + 37566, + 58634, + 29477, + 35557, + 58862, + 29866, + 33501, + 59245, + 30478, + 31376, + 59698, + 31179, + 29204, + 57494, + 26127, + 60771, + 58098, + 26073, + 58880, + 58683, + 25994, + 57090, + 59077, + 26006, + 55368, + 59455, + 26025, + 53658, + 59644, + 26124, + 51866, + 59805, + 26219, + 50057, + 59985, + 26318, + 48271, + 60156, + 26438, + 46493, + 60284, + 26606, + 44680, + 60432, + 26836, + 42833, + 60545, + 27078, + 40948, + 60683, + 27390, + 39000, + 60826, + 27699, + 37042, + 61089, + 28164, + 34928, + 61414, + 28654, + 32803, + 62078, + 29344, + 30473, + 27344, + 53107, + 48639, + 27535, + 53434, + 46766, + 27568, + 53884, + 44918, + 27291, + 54537, + 42835, + 27337, + 54879, + 40912, + 27252, + 55148, + 38750, + 27156, + 55396, + 36624, + 27051, + 55573, + 34618, + 26989, + 55699, + 32640, + 26809, + 55889, + 30856, + 26717, + 56004, + 29057, + 26563, + 56157, + 27458, + 26282, + 56358, + 25870, + 26259, + 56390, + 24306, + 26093, + 56350, + 22613, + 26134, + 56225, + 20929, + 26117, + 56121, + 19241, + 28625, + 51976, + 49237, + 28963, + 52158, + 47291, + 29260, + 52376, + 45422, + 29401, + 52690, + 43406, + 29466, + 53052, + 41368, + 29483, + 53383, + 39184, + 29454, + 53692, + 36981, + 29365, + 53990, + 34920, + 29252, + 54266, + 32884, + 29089, + 54517, + 31123, + 28922, + 54742, + 29359, + 28784, + 54962, + 27750, + 28656, + 55165, + 26216, + 28522, + 55351, + 24682, + 28423, + 55303, + 22953, + 28328, + 55242, + 21192, + 28333, + 55119, + 19486, + 29905, + 50928, + 49897, + 30358, + 50978, + 47826, + 30744, + 51082, + 45876, + 31004, + 51282, + 43859, + 31148, + 51560, + 41791, + 31249, + 51853, + 39657, + 31322, + 52164, + 37466, + 31321, + 52512, + 35319, + 31283, + 52879, + 33170, + 31157, + 53251, + 31354, + 30998, + 53542, + 29631, + 30857, + 53819, + 28007, + 30736, + 54082, + 26491, + 30608, + 54325, + 24975, + 30496, + 54321, + 23267, + 30400, + 54252, + 21456, + 30390, + 54110, + 19704, + 31206, + 49877, + 50668, + 31703, + 49853, + 48409, + 32156, + 49869, + 46396, + 32540, + 49951, + 44370, + 32792, + 50163, + 42279, + 32999, + 50404, + 40178, + 33144, + 50671, + 38015, + 33217, + 50976, + 35878, + 33224, + 51313, + 33750, + 33168, + 51684, + 31838, + 33055, + 52069, + 30089, + 32921, + 52450, + 28373, + 32795, + 52839, + 26804, + 32649, + 53207, + 25269, + 32536, + 53276, + 23607, + 32455, + 53144, + 21779, + 32454, + 52980, + 19954, + 32560, + 48748, + 51775, + 33167, + 48640, + 49066, + 33706, + 48607, + 47041, + 34206, + 48592, + 45018, + 34520, + 48803, + 42898, + 34792, + 49037, + 40786, + 34954, + 49273, + 38610, + 35089, + 49508, + 36470, + 35136, + 49796, + 34392, + 35145, + 50122, + 32397, + 35081, + 50531, + 30672, + 34988, + 50925, + 28945, + 34900, + 51302, + 27317, + 34791, + 51675, + 25724, + 34697, + 51947, + 24097, + 34715, + 51882, + 22289, + 34744, + 51830, + 20335, + 34334, + 47343, + 52904, + 35034, + 47170, + 50040, + 35560, + 47106, + 47870, + 36036, + 47099, + 45806, + 36425, + 47185, + 43702, + 36744, + 47334, + 41574, + 36952, + 47551, + 39405, + 37103, + 47794, + 37219, + 37149, + 48110, + 35130, + 37169, + 48421, + 33042, + 37090, + 48913, + 31232, + 37015, + 49376, + 29501, + 36961, + 49764, + 27832, + 36896, + 50126, + 26203, + 36805, + 50492, + 24595, + 36883, + 50528, + 22781, + 36961, + 50574, + 20914, + 36067, + 46063, + 53671, + 36783, + 45803, + 51053, + 37419, + 45630, + 48789, + 37928, + 45632, + 46713, + 38395, + 45646, + 44633, + 38766, + 45727, + 42483, + 39055, + 45853, + 40323, + 39186, + 46068, + 38130, + 39277, + 46341, + 36007, + 39318, + 46677, + 33967, + 39300, + 47080, + 32039, + 39210, + 47579, + 30271, + 39105, + 48065, + 28509, + 39068, + 48464, + 26798, + 39002, + 48873, + 25117, + 39059, + 49084, + 23337, + 39185, + 49196, + 21477, + 37888, + 44727, + 54555, + 38751, + 44324, + 52078, + 39459, + 44088, + 49853, + 40008, + 43998, + 47719, + 40476, + 43955, + 45613, + 40866, + 43993, + 43454, + 41220, + 44073, + 41279, + 41326, + 44275, + 39073, + 41380, + 44497, + 36857, + 41438, + 44868, + 34814, + 41487, + 45217, + 32777, + 41444, + 45730, + 31034, + 41365, + 46231, + 29286, + 41356, + 46627, + 27533, + 41372, + 46978, + 25796, + 41436, + 47302, + 24069, + 41656, + 47545, + 22253, + 40186, + 42889, + 55731, + 41053, + 42513, + 52995, + 41730, + 42385, + 50888, + 42324, + 42277, + 48788, + 42747, + 42242, + 46656, + 43117, + 42235, + 44511, + 43395, + 42307, + 42301, + 43597, + 42439, + 40117, + 43704, + 42661, + 37969, + 43753, + 42956, + 35882, + 43748, + 43314, + 33850, + 43725, + 43747, + 31926, + 43679, + 44282, + 30138, + 43629, + 44799, + 28348, + 43701, + 45148, + 26522, + 43765, + 45480, + 24746, + 44017, + 45828, + 22941, + 42716, + 41056, + 56706, + 43501, + 40803, + 54146, + 44137, + 40647, + 51956, + 44698, + 40521, + 49858, + 45116, + 40459, + 47734, + 45453, + 40436, + 45596, + 45671, + 40488, + 43377, + 45837, + 40566, + 41129, + 45947, + 40794, + 39025, + 46046, + 41025, + 36925, + 46057, + 41409, + 34947, + 46037, + 41776, + 32953, + 46046, + 42248, + 31133, + 46031, + 42736, + 29345, + 46120, + 43171, + 27495, + 46252, + 43554, + 25615, + 46432, + 43977, + 23699, + 45309, + 39191, + 57721, + 45996, + 39034, + 55334, + 46603, + 38893, + 53112, + 47112, + 38794, + 51005, + 47570, + 38714, + 48887, + 47898, + 38671, + 46737, + 48174, + 38664, + 44579, + 48307, + 38787, + 42385, + 48399, + 38955, + 40222, + 48430, + 39196, + 38109, + 48441, + 39487, + 36055, + 48428, + 39853, + 34091, + 48419, + 40252, + 32162, + 48401, + 40695, + 30313, + 48387, + 41133, + 28478, + 48678, + 41559, + 26530, + 48921, + 41981, + 24563, + 47636, + 37446, + 58926, + 48363, + 37277, + 56470, + 48936, + 37146, + 54261, + 49439, + 37050, + 52099, + 49885, + 36976, + 49968, + 50225, + 36927, + 47840, + 50493, + 36901, + 45707, + 50639, + 36972, + 43565, + 50730, + 37082, + 41417, + 50783, + 37311, + 39328, + 50815, + 37559, + 37240, + 50836, + 37879, + 35260, + 50836, + 38218, + 33299, + 50935, + 38662, + 31394, + 51008, + 39123, + 29523, + 51193, + 39624, + 27602, + 51509, + 40146, + 25606, + 49917, + 35717, + 59976, + 50633, + 35582, + 57512, + 51173, + 35477, + 55351, + 51695, + 35373, + 53213, + 52138, + 35280, + 51079, + 52545, + 35200, + 48949, + 52779, + 35189, + 46835, + 52974, + 35201, + 44727, + 53032, + 35314, + 42645, + 53079, + 35451, + 40560, + 53103, + 35690, + 38479, + 53131, + 35933, + 36422, + 53189, + 36207, + 34467, + 53253, + 36511, + 32514, + 53471, + 37009, + 30595, + 53638, + 37514, + 28704, + 53995, + 38212, + 26692, + 52072, + 34013, + 60811, + 52720, + 33911, + 58493, + 53271, + 33806, + 56377, + 53749, + 33691, + 54214, + 54171, + 33592, + 52065, + 54541, + 33508, + 49932, + 54810, + 33469, + 47843, + 55023, + 33454, + 45776, + 55156, + 33534, + 43748, + 55243, + 33656, + 41736, + 55329, + 33849, + 39730, + 55406, + 34081, + 37723, + 55517, + 34345, + 35719, + 55632, + 34640, + 33715, + 55815, + 35096, + 31722, + 56046, + 35648, + 29741, + 56319, + 36235, + 27709, + 53954, + 32294, + 61680, + 54627, + 32206, + 59315, + 55163, + 32098, + 57238, + 55674, + 31956, + 55069, + 56156, + 31824, + 52940, + 56507, + 31744, + 50872, + 56821, + 31679, + 48824, + 57020, + 31681, + 46819, + 57205, + 31702, + 44821, + 57320, + 31844, + 42858, + 57430, + 31984, + 40894, + 57543, + 32216, + 38951, + 57649, + 32427, + 37001, + 57868, + 32689, + 34950, + 58081, + 32896, + 32896, + 58438, + 33660, + 30884, + 58769, + 34321, + 28838, + 56246, + 30378, + 61938, + 56846, + 30283, + 59783, + 57373, + 30174, + 57758, + 57840, + 30100, + 55751, + 58269, + 30042, + 53769, + 58590, + 29992, + 51799, + 58875, + 29939, + 49843, + 59097, + 29945, + 47921, + 59287, + 29982, + 46013, + 59452, + 30090, + 44095, + 59588, + 30258, + 42160, + 59715, + 30468, + 40213, + 59834, + 30735, + 38256, + 59985, + 31009, + 36265, + 60231, + 31313, + 34215, + 60542, + 31701, + 32143, + 61009, + 32378, + 29958, + 58712, + 28362, + 62202, + 59261, + 28286, + 60178, + 59781, + 28204, + 58242, + 60200, + 28180, + 56364, + 60550, + 28191, + 54537, + 60855, + 28197, + 52709, + 61101, + 28167, + 50868, + 61335, + 28147, + 49044, + 61513, + 28213, + 47233, + 61680, + 28284, + 45414, + 61823, + 28448, + 43521, + 61951, + 28636, + 41593, + 62085, + 28931, + 39631, + 62225, + 29238, + 37658, + 62455, + 29615, + 35609, + 62762, + 29959, + 33524, + 63859, + 30489, + 31081, + 29491, + 54004, + 49446, + 29698, + 54274, + 47590, + 29844, + 54607, + 45798, + 29807, + 55038, + 43895, + 29746, + 55432, + 41953, + 29611, + 55818, + 39947, + 29448, + 56132, + 37846, + 29256, + 56388, + 35836, + 29046, + 56588, + 33889, + 28845, + 56748, + 32056, + 28658, + 56880, + 30329, + 28478, + 56999, + 28611, + 28302, + 57106, + 27077, + 28127, + 57198, + 25539, + 27972, + 57223, + 23963, + 27849, + 57155, + 22332, + 27724, + 57087, + 20664, + 30873, + 52860, + 49935, + 31224, + 52993, + 48004, + 31469, + 53248, + 46204, + 31600, + 53551, + 44359, + 31563, + 53912, + 42360, + 31507, + 54281, + 40345, + 31455, + 54612, + 38223, + 31347, + 54928, + 36155, + 31188, + 55203, + 34166, + 31005, + 55450, + 32284, + 30816, + 55663, + 30581, + 30624, + 55854, + 28866, + 30465, + 56024, + 27341, + 30306, + 56175, + 25828, + 30148, + 56276, + 24287, + 30014, + 56193, + 22610, + 29877, + 56110, + 20899, + 32179, + 51817, + 50610, + 32604, + 51830, + 48537, + 32930, + 52005, + 46684, + 33217, + 52221, + 44851, + 33278, + 52527, + 42818, + 33291, + 52855, + 40798, + 33321, + 53207, + 38643, + 33304, + 53549, + 36531, + 33208, + 53862, + 34473, + 33074, + 54157, + 32504, + 32893, + 54425, + 30825, + 32704, + 54675, + 29133, + 32547, + 54910, + 27598, + 32396, + 55122, + 26101, + 32240, + 55315, + 24604, + 32094, + 55216, + 22899, + 31954, + 55110, + 21151, + 33598, + 50803, + 51382, + 34094, + 50767, + 49188, + 34471, + 50885, + 47280, + 34815, + 51017, + 45393, + 35009, + 51262, + 43388, + 35139, + 51545, + 41382, + 35204, + 51840, + 39263, + 35229, + 52153, + 37117, + 35176, + 52487, + 35035, + 35093, + 52838, + 32944, + 34934, + 53200, + 31212, + 34758, + 53497, + 29533, + 34594, + 53774, + 27944, + 34447, + 54038, + 26434, + 34298, + 54285, + 24926, + 34190, + 54269, + 23252, + 34095, + 54160, + 21480, + 35163, + 49774, + 52178, + 35623, + 49725, + 49956, + 36032, + 49748, + 47920, + 36398, + 49818, + 45962, + 36682, + 49976, + 43960, + 36889, + 50213, + 41931, + 37024, + 50464, + 39857, + 37087, + 50718, + 37718, + 37088, + 50996, + 35648, + 37046, + 51290, + 33599, + 36950, + 51651, + 31774, + 36811, + 52027, + 30079, + 36653, + 52386, + 28406, + 36491, + 52743, + 26832, + 36307, + 53091, + 25276, + 36231, + 53228, + 23636, + 36259, + 53179, + 21803, + 36848, + 48606, + 53016, + 37355, + 48530, + 50800, + 37838, + 48505, + 48708, + 38236, + 48587, + 46718, + 38588, + 48693, + 44728, + 38785, + 48919, + 42638, + 38930, + 49151, + 40547, + 39006, + 49335, + 38417, + 39044, + 49541, + 36332, + 39017, + 49792, + 34332, + 38963, + 50084, + 32409, + 38861, + 50476, + 30724, + 38739, + 50853, + 29033, + 38644, + 51194, + 27411, + 38535, + 51532, + 25811, + 38433, + 51833, + 24189, + 38461, + 51941, + 22371, + 38653, + 47398, + 53955, + 39264, + 47240, + 51746, + 39797, + 47153, + 49664, + 40225, + 47168, + 47642, + 40602, + 47214, + 45642, + 40868, + 47335, + 43558, + 41073, + 47488, + 41454, + 41132, + 47676, + 39306, + 41147, + 47870, + 37144, + 41111, + 48161, + 35117, + 41047, + 48443, + 33083, + 40898, + 48899, + 31299, + 40756, + 49330, + 29588, + 40686, + 49677, + 27941, + 40654, + 49987, + 26327, + 40602, + 50308, + 24726, + 40661, + 50512, + 22914, + 40597, + 46022, + 54922, + 41186, + 45824, + 52710, + 41750, + 45748, + 50682, + 42249, + 45692, + 48668, + 42632, + 45707, + 46637, + 42961, + 45739, + 44584, + 43187, + 45834, + 42436, + 43327, + 45964, + 40284, + 43324, + 46170, + 38118, + 43301, + 46420, + 36033, + 43248, + 46722, + 34041, + 43180, + 47093, + 32158, + 43074, + 47573, + 30429, + 42929, + 48045, + 28684, + 42904, + 48376, + 26972, + 42858, + 48714, + 25282, + 42886, + 48970, + 23493, + 42962, + 44494, + 56094, + 43596, + 44330, + 53788, + 44114, + 44222, + 51733, + 44581, + 44121, + 49726, + 44919, + 44091, + 47678, + 45194, + 44094, + 45616, + 45383, + 44157, + 43452, + 45530, + 44241, + 41253, + 45549, + 44436, + 39074, + 45531, + 44643, + 36883, + 45404, + 44941, + 34821, + 45288, + 45225, + 32787, + 45281, + 45714, + 31137, + 45232, + 46193, + 29462, + 45220, + 46579, + 27732, + 45250, + 46892, + 25988, + 45286, + 47205, + 24207, + 45508, + 42679, + 57256, + 46100, + 42579, + 54959, + 46595, + 42483, + 52857, + 46983, + 42410, + 50826, + 47319, + 42358, + 48777, + 47547, + 42354, + 46666, + 47733, + 42380, + 44530, + 47816, + 42504, + 42319, + 47859, + 42663, + 40138, + 47847, + 42871, + 37996, + 47810, + 43146, + 35937, + 47736, + 43482, + 33960, + 47661, + 43861, + 32058, + 47591, + 44321, + 30288, + 47509, + 44785, + 28525, + 47570, + 45131, + 26689, + 47616, + 45460, + 24869, + 48011, + 40951, + 58491, + 48570, + 40817, + 56234, + 49032, + 40704, + 54138, + 49380, + 40642, + 52039, + 49661, + 40610, + 49929, + 49885, + 40580, + 47793, + 50070, + 40552, + 45639, + 50140, + 40652, + 43453, + 50167, + 40795, + 41249, + 50124, + 40997, + 39096, + 50082, + 41211, + 36954, + 50056, + 41581, + 35030, + 50008, + 41941, + 33095, + 49970, + 42347, + 31272, + 49917, + 42763, + 29478, + 49989, + 43173, + 27626, + 50130, + 43561, + 25688, + 50312, + 39246, + 59590, + 50887, + 39111, + 57318, + 51289, + 39034, + 55232, + 51674, + 38963, + 53160, + 51996, + 38916, + 51061, + 52295, + 38873, + 48964, + 52437, + 38879, + 46812, + 52548, + 38906, + 44661, + 52549, + 39014, + 42506, + 52541, + 39156, + 40364, + 52514, + 39377, + 38257, + 52478, + 39639, + 36199, + 52418, + 39985, + 34233, + 52355, + 40360, + 32289, + 52308, + 40778, + 30433, + 52252, + 41183, + 28597, + 52609, + 41664, + 26626, + 52383, + 37549, + 60599, + 52923, + 37397, + 58343, + 53365, + 37285, + 56245, + 53746, + 37214, + 54166, + 54081, + 37153, + 52072, + 54376, + 37104, + 49962, + 54559, + 37106, + 47836, + 54678, + 37140, + 45700, + 54732, + 37218, + 43602, + 54753, + 37313, + 41516, + 54764, + 37501, + 39446, + 54763, + 37713, + 37376, + 54765, + 38011, + 35386, + 54751, + 38338, + 33415, + 54837, + 38768, + 31496, + 54920, + 39220, + 29609, + 55101, + 39731, + 27649, + 54334, + 35887, + 61540, + 54854, + 35769, + 59288, + 55291, + 35666, + 57207, + 55685, + 35573, + 55104, + 56062, + 35481, + 53008, + 56346, + 35427, + 50911, + 56598, + 35386, + 48820, + 56730, + 35409, + 46731, + 56847, + 35448, + 44660, + 56912, + 35549, + 42648, + 56974, + 35665, + 40638, + 57024, + 35865, + 38631, + 57064, + 36075, + 36613, + 57069, + 36393, + 34567, + 57061, + 36738, + 32526, + 57275, + 37245, + 30572, + 57474, + 37742, + 28598, + 56055, + 34273, + 62394, + 56598, + 34186, + 60229, + 57033, + 34064, + 58163, + 57446, + 33939, + 56050, + 57831, + 33833, + 53906, + 58136, + 33768, + 51820, + 58405, + 33720, + 49768, + 58604, + 33713, + 47733, + 58777, + 33720, + 45707, + 58899, + 33795, + 43715, + 58996, + 33897, + 41732, + 59096, + 34062, + 39758, + 59197, + 34264, + 37785, + 59336, + 34509, + 35781, + 59494, + 34799, + 33753, + 59757, + 35277, + 31722, + 60137, + 35872, + 29646, + 57844, + 32692, + 63276, + 58189, + 32659, + 61163, + 58687, + 32498, + 59054, + 59128, + 32369, + 56960, + 59567, + 32245, + 54796, + 59948, + 32147, + 52702, + 60236, + 32086, + 50715, + 60506, + 32037, + 48745, + 60714, + 32036, + 46793, + 60911, + 32045, + 44845, + 61015, + 32153, + 42876, + 61117, + 32255, + 40903, + 61217, + 32445, + 38914, + 61311, + 32616, + 36921, + 61565, + 32794, + 34909, + 61808, + 32896, + 32896, + 62932, + 33781, + 30625, + 60254, + 30635, + 63605, + 60632, + 30637, + 61518, + 61053, + 30539, + 59500, + 61447, + 30446, + 57494, + 61796, + 30399, + 55525, + 62122, + 30361, + 53575, + 62399, + 30311, + 51697, + 62670, + 30269, + 49849, + 62894, + 30276, + 47991, + 63086, + 30308, + 46118, + 63251, + 30408, + 44214, + 63394, + 30578, + 42273, + 63538, + 30764, + 40315, + 63681, + 30971, + 38336, + 63849, + 31166, + 36324, + 64107, + 31326, + 34248, + 65057, + 31668, + 31825, + 31657, + 55190, + 50562, + 31949, + 55114, + 48374, + 32090, + 55388, + 46594, + 32183, + 55730, + 44851, + 32063, + 56183, + 42932, + 31859, + 56649, + 41045, + 31686, + 57010, + 39016, + 31457, + 57341, + 36997, + 31202, + 57660, + 35117, + 30917, + 57896, + 33205, + 30698, + 58029, + 31499, + 30485, + 58098, + 29818, + 30284, + 58162, + 28210, + 30116, + 58240, + 26724, + 29954, + 58326, + 25234, + 29765, + 58292, + 23670, + 29586, + 58172, + 22031, + 33199, + 53938, + 50828, + 33590, + 53917, + 48778, + 33773, + 54199, + 47023, + 33903, + 54485, + 45291, + 33859, + 54844, + 43389, + 33757, + 55246, + 41486, + 33654, + 55603, + 39465, + 33518, + 55928, + 37402, + 33310, + 56202, + 35439, + 33070, + 56450, + 33516, + 32831, + 56650, + 31762, + 32596, + 56830, + 30080, + 32374, + 56994, + 28436, + 32196, + 57119, + 26953, + 32037, + 57243, + 25476, + 31846, + 57262, + 23935, + 31719, + 57189, + 22341, + 34657, + 52885, + 51341, + 35150, + 52865, + 49350, + 35402, + 53089, + 47539, + 35598, + 53349, + 45764, + 35669, + 53654, + 43904, + 35635, + 54000, + 41988, + 35576, + 54350, + 40002, + 35486, + 54671, + 37901, + 35342, + 54972, + 35876, + 35140, + 55244, + 33903, + 34919, + 55495, + 32085, + 34690, + 55721, + 30424, + 34460, + 55932, + 28759, + 34290, + 56128, + 27261, + 34131, + 56312, + 25772, + 33981, + 56436, + 24269, + 33936, + 56406, + 22706, + 36182, + 51882, + 52051, + 36645, + 51860, + 50045, + 37014, + 52000, + 48156, + 37251, + 52224, + 46323, + 37459, + 52490, + 44490, + 37488, + 52770, + 42537, + 37496, + 53073, + 40584, + 37425, + 53389, + 38470, + 37313, + 53691, + 36390, + 37152, + 53976, + 34390, + 36971, + 54261, + 32468, + 36751, + 54525, + 30813, + 36526, + 54776, + 29156, + 36350, + 55038, + 27616, + 36199, + 55294, + 26115, + 36036, + 55532, + 24617, + 36073, + 55547, + 23048, + 37824, + 50956, + 52787, + 38298, + 50996, + 50773, + 38723, + 51071, + 48831, + 39006, + 51237, + 46971, + 39294, + 51411, + 45152, + 39366, + 51604, + 43184, + 39397, + 51790, + 41228, + 39373, + 52039, + 39180, + 39354, + 52335, + 37145, + 39196, + 52605, + 35160, + 39051, + 52906, + 33186, + 38854, + 53224, + 31438, + 38636, + 53496, + 29733, + 38468, + 53788, + 28118, + 38334, + 54090, + 26586, + 38184, + 54381, + 25057, + 38288, + 54588, + 23469, + 39546, + 50056, + 53563, + 40039, + 50039, + 51514, + 40499, + 50066, + 49575, + 40834, + 50163, + 47681, + 41145, + 50278, + 45829, + 41306, + 50407, + 43895, + 41353, + 50546, + 41900, + 41346, + 50711, + 39874, + 41323, + 50928, + 37832, + 41198, + 51143, + 35837, + 41050, + 51382, + 33893, + 40905, + 51679, + 32083, + 40731, + 52008, + 30393, + 40573, + 52340, + 28720, + 40473, + 52674, + 27139, + 40355, + 53018, + 25570, + 40326, + 53382, + 23980, + 41385, + 48948, + 54367, + 41960, + 48866, + 52362, + 42447, + 48846, + 50478, + 42866, + 48862, + 48600, + 43145, + 48950, + 46683, + 43375, + 49052, + 44757, + 43431, + 49212, + 42728, + 43441, + 49356, + 40701, + 43359, + 49535, + 38624, + 43273, + 49734, + 36581, + 43122, + 49969, + 34629, + 42993, + 50236, + 32717, + 42818, + 50590, + 31004, + 42644, + 50940, + 29307, + 42555, + 51272, + 27679, + 42494, + 51595, + 26093, + 42526, + 52008, + 24576, + 43616, + 47618, + 55438, + 44185, + 47487, + 53410, + 44665, + 47469, + 51518, + 45069, + 47436, + 49644, + 45336, + 47481, + 47704, + 45522, + 47535, + 45737, + 45619, + 47641, + 43700, + 45642, + 47759, + 41622, + 45570, + 47940, + 39537, + 45455, + 48144, + 37449, + 45309, + 48403, + 35489, + 45156, + 48684, + 33567, + 44986, + 49065, + 31749, + 44798, + 49451, + 29978, + 44659, + 49803, + 28261, + 44619, + 50082, + 26611, + 44622, + 50417, + 24997, + 46034, + 46159, + 56539, + 46529, + 46093, + 54530, + 47040, + 46097, + 52603, + 47320, + 46051, + 50679, + 47574, + 46040, + 48745, + 47712, + 46088, + 46729, + 47823, + 46151, + 44700, + 47817, + 46263, + 42548, + 47790, + 46401, + 40412, + 47659, + 46562, + 38266, + 47523, + 46771, + 36207, + 47366, + 47039, + 34283, + 47218, + 47347, + 32417, + 47069, + 47741, + 30695, + 46906, + 48138, + 28959, + 46843, + 48469, + 27215, + 46800, + 48811, + 25475, + 48566, + 44572, + 57793, + 48999, + 44493, + 55742, + 49414, + 44457, + 53783, + 49667, + 44439, + 51811, + 49876, + 44435, + 49840, + 50003, + 44472, + 47807, + 50076, + 44525, + 45738, + 50057, + 44620, + 43579, + 50005, + 44743, + 41377, + 49893, + 44902, + 39214, + 49772, + 45066, + 37051, + 49644, + 45340, + 35098, + 49500, + 45614, + 33158, + 49382, + 45976, + 31414, + 49249, + 46343, + 29695, + 49159, + 46692, + 27945, + 49137, + 47038, + 26130, + 51090, + 42932, + 59128, + 51475, + 42817, + 57006, + 51842, + 42836, + 55056, + 52055, + 42779, + 53076, + 52247, + 42771, + 51055, + 52395, + 42742, + 49021, + 52424, + 42779, + 46886, + 52409, + 42799, + 44731, + 52321, + 42896, + 42534, + 52237, + 43036, + 40366, + 52119, + 43229, + 38246, + 51992, + 43469, + 36183, + 51850, + 43786, + 34214, + 51721, + 44118, + 32293, + 51617, + 44503, + 30499, + 51496, + 44888, + 28718, + 51539, + 45218, + 26785, + 53353, + 41149, + 60281, + 53661, + 41055, + 58201, + 53923, + 41025, + 56189, + 54141, + 41008, + 54195, + 54331, + 40989, + 52163, + 54466, + 40956, + 50081, + 54551, + 40963, + 47965, + 54582, + 40991, + 45813, + 54551, + 41070, + 43662, + 54483, + 41177, + 41505, + 54419, + 41352, + 39390, + 54353, + 41550, + 37292, + 54268, + 41877, + 35307, + 54135, + 42202, + 33329, + 54072, + 42582, + 31470, + 54004, + 42965, + 29659, + 53997, + 43308, + 27740, + 55292, + 39434, + 61221, + 55554, + 39363, + 59148, + 55812, + 39317, + 57125, + 56086, + 39268, + 55111, + 56349, + 39220, + 53100, + 56494, + 39201, + 51015, + 56628, + 39191, + 48930, + 56676, + 39243, + 46816, + 56715, + 39305, + 44709, + 56672, + 39397, + 42626, + 56629, + 39514, + 40543, + 56595, + 39699, + 38477, + 56559, + 39920, + 36429, + 56514, + 40263, + 34448, + 56470, + 40614, + 32481, + 56478, + 41012, + 30589, + 56510, + 41483, + 28692, + 57115, + 37742, + 62160, + 57440, + 37677, + 60062, + 57689, + 37636, + 58037, + 57941, + 37583, + 56007, + 58186, + 37521, + 53981, + 58370, + 37487, + 51925, + 58529, + 37467, + 49856, + 58638, + 37503, + 47784, + 58723, + 37567, + 45708, + 58767, + 37635, + 43662, + 58806, + 37718, + 41633, + 58836, + 37875, + 39608, + 58871, + 38068, + 37586, + 58923, + 38365, + 35577, + 58948, + 38695, + 33575, + 59103, + 39141, + 31622, + 59311, + 39677, + 29501, + 58643, + 36205, + 63166, + 58973, + 36135, + 61027, + 59252, + 36072, + 58983, + 59518, + 36009, + 56937, + 59785, + 35935, + 54870, + 60036, + 35869, + 52810, + 60240, + 35838, + 50772, + 60425, + 35820, + 48742, + 60547, + 35856, + 46719, + 60659, + 35898, + 44700, + 60749, + 35974, + 42694, + 60842, + 36065, + 40687, + 60932, + 36264, + 38686, + 61027, + 36474, + 36678, + 61144, + 36785, + 34654, + 61293, + 37112, + 32626, + 62042, + 37677, + 30460, + 60283, + 34640, + 64050, + 60583, + 34593, + 61957, + 60870, + 34534, + 59911, + 61129, + 34470, + 57875, + 61415, + 34383, + 55787, + 61732, + 34290, + 53678, + 61993, + 34240, + 51672, + 62248, + 34223, + 49707, + 62461, + 34235, + 47743, + 62648, + 34246, + 45777, + 62804, + 34318, + 43809, + 62944, + 34414, + 41836, + 63099, + 34566, + 39864, + 63252, + 34749, + 37891, + 63523, + 34998, + 35914, + 63987, + 35356, + 33923, + 65164, + 35665, + 31484, + 62110, + 32941, + 64922, + 62347, + 32971, + 62860, + 62606, + 32957, + 60839, + 62861, + 32889, + 58790, + 63116, + 32807, + 56729, + 63441, + 32674, + 54610, + 63780, + 32563, + 52592, + 64036, + 32531, + 50680, + 64289, + 32507, + 48773, + 64505, + 32503, + 46851, + 64714, + 32516, + 44925, + 64884, + 32615, + 42973, + 65052, + 32720, + 41015, + 65215, + 32839, + 39053, + 65439, + 32915, + 37075, + 65535, + 33009, + 35031, + 65535, + 32896, + 32896 + ], + "gridPoints": [ + 17, + 17, + 17 + ], + "precision": 2 + }, + "inputChannels": 3, + "mCurves": [ + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + } + ], + "matrix": { + "e": [ + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0 + ] + }, + "outputChannels": 3, + "type": "lutAtoBType" + } + } + }, + { + "AToB1Tag": { + "data": { + "aCurves": [ + { + "functionType": 4, + "params": [ + 3.0517578125e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 3.0517578125e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 3.0517578125e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + } + ], + "bCurves": [ + { + "curveType": "table", + "precision": 2, + "table": [ + 0, + 65535 + ], + "type": "Curve" + }, + { + "curveType": "table", + "precision": 2, + "table": [ + 0, + 65535 + ], + "type": "Curve" + }, + { + "curveType": "table", + "precision": 2, + "table": [ + 0, + 65535 + ], + "type": "Curve" + } + ], + "clut": { + "data": [ + 0, + 0, + 0, + 9375, + 3972, + 46786, + 25238, + 46982, + 6361, + 34613, + 50954, + 53148, + 28576, + 14581, + 912, + 37950, + 18553, + 47699, + 53814, + 61563, + 7274, + 63189, + 65535, + 54060 + ], + "gridPoints": [ + 2, + 2, + 2 + ], + "precision": 2 + }, + "inputChannels": 3, + "mCurves": [ + { + "functionType": 4, + "params": [ + 0.0, + 1.52587890625e-05, + 0.0, + 0.0001373291015625, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 0.0, + 1.52587890625e-05, + 0.0, + 0.0001373291015625, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 0.0, + 1.52587890625e-05, + 0.0, + 0.000152587890625, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + } + ], + "matrix": { + "e": [ + 0.0, + 1.0, + 0.0, + 1.690338134765625, + -1.690338134765625, + 0.0, + 0.0, + 0.6761322021484375, + -0.6761322021484375, + 0.0, + 0.5019683837890625, + 0.5019683837890625 + ] + }, + "outputChannels": 3, + "type": "lutAtoBType" + } + } + }, + { + "BToA0Tag": { + "data": { + "aCurves": [ + { + "functionType": 1, + "params": [ + 1.52587890625e-05, + 1.52587890625e-05, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 1, + "params": [ + 1.52587890625e-05, + 1.52587890625e-05, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 1, + "params": [ + 1.52587890625e-05, + 1.52587890625e-05, + 0.0 + ], + "type": "ParametricCurve" + } + ], + "bCurves": [ + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + } + ], + "clut": { + "data": [ + 6975, + 41668, + 43636, + 8089, + 41050, + 41937, + 8299, + 40718, + 41242, + 8826, + 40352, + 39848, + 9573, + 39643, + 38082, + 9272, + 40012, + 36686, + 9972, + 39601, + 35466, + 9917, + 39295, + 34836, + 10355, + 36465, + 29818, + 10472, + 36010, + 26275, + 10421, + 35933, + 25745, + 10368, + 35857, + 25209, + 8430, + 34739, + 19141, + 8461, + 35017, + 10117, + 8597, + 35104, + 9389, + 8735, + 35182, + 8755, + 8289, + 34691, + 7379, + 6090, + 41286, + 47034, + 6777, + 40846, + 43259, + 8211, + 40209, + 41050, + 8466, + 39761, + 40171, + 9206, + 39326, + 38251, + 9618, + 38684, + 36770, + 9257, + 39110, + 35210, + 10725, + 34404, + 29978, + 10649, + 35513, + 29356, + 10191, + 35804, + 25654, + 10451, + 35544, + 25304, + 10289, + 35212, + 23800, + 8645, + 32613, + 10474, + 8880, + 32982, + 9757, + 8719, + 34551, + 8780, + 8927, + 34709, + 8164, + 9117, + 34844, + 7641, + 5808, + 40717, + 46970, + 5629, + 40133, + 45842, + 6705, + 39660, + 42427, + 8375, + 39013, + 39780, + 8698, + 38375, + 38605, + 9493, + 37884, + 36315, + 9038, + 33733, + 30836, + 10183, + 34885, + 30731, + 10518, + 33891, + 27552, + 10723, + 34017, + 25392, + 10525, + 34389, + 24081, + 9134, + 32214, + 18700, + 9119, + 30822, + 10168, + 9162, + 32286, + 9284, + 9368, + 32991, + 8573, + 9316, + 34237, + 7624, + 9505, + 34421, + 7114, + 4066, + 40392, + 51256, + 4642, + 39109, + 46432, + 5633, + 38548, + 44090, + 6753, + 37028, + 40099, + 7551, + 36810, + 38030, + 8006, + 33100, + 32235, + 9204, + 32260, + 30073, + 10073, + 31763, + 27932, + 10539, + 31090, + 25355, + 10594, + 29563, + 20847, + 10495, + 30329, + 19761, + 9525, + 28065, + 10854, + 9964, + 29788, + 9932, + 9718, + 30528, + 8930, + 9793, + 31994, + 8143, + 9993, + 33001, + 7361, + 9978, + 33927, + 6511, + 3123, + 38091, + 51316, + 4174, + 35631, + 45753, + 4481, + 34907, + 42342, + 5401, + 33981, + 38970, + 7055, + 31493, + 34104, + 7480, + 29584, + 30052, + 8850, + 29457, + 28172, + 10137, + 28373, + 25464, + 10571, + 26879, + 21187, + 10533, + 27921, + 20173, + 9968, + 26331, + 14003, + 9564, + 26127, + 9643, + 10062, + 27289, + 8894, + 10442, + 29254, + 8565, + 10421, + 30256, + 7757, + 10497, + 31712, + 7063, + 22473, + 35726, + 5445, + 2360, + 35194, + 51805, + 2581, + 35005, + 49269, + 4007, + 31883, + 41390, + 5252, + 29943, + 36429, + 6544, + 28485, + 33155, + 6831, + 26603, + 28957, + 8420, + 25605, + 25565, + 10003, + 25049, + 22910, + 10255, + 24300, + 19381, + 10406, + 22680, + 14585, + 10318, + 22528, + 10411, + 10341, + 23817, + 9160, + 10125, + 25319, + 7421, + 10575, + 26728, + 7156, + 17812, + 29189, + 6585, + 22731, + 32714, + 6222, + 26945, + 36443, + 6586, + 1796, + 32174, + 52129, + 1621, + 32248, + 49826, + 3524, + 28411, + 40448, + 4642, + 27054, + 36214, + 5793, + 24792, + 31453, + 5755, + 22956, + 27280, + 7983, + 21687, + 23347, + 9493, + 20908, + 20250, + 10436, + 19943, + 16000, + 11016, + 18894, + 11488, + 10885, + 20166, + 10189, + 11349, + 21248, + 7521, + 14847, + 23270, + 5986, + 18908, + 26458, + 6049, + 22846, + 29236, + 6312, + 25641, + 31769, + 6403, + 28689, + 34783, + 6415, + 556, + 27734, + 52792, + 362, + 27272, + 50276, + 2539, + 23866, + 40594, + 2723, + 22399, + 34840, + 3886, + 20857, + 29759, + 4916, + 18478, + 24716, + 7692, + 17377, + 21411, + 9339, + 16505, + 17518, + 10632, + 15660, + 13574, + 11486, + 16110, + 11089, + 13384, + 17715, + 9044, + 16074, + 19906, + 7417, + 19389, + 22648, + 6410, + 22143, + 25055, + 5831, + 25460, + 28227, + 6396, + 29025, + 31048, + 6614, + 32480, + 34021, + 6665, + 3165, + 23366, + 52883, + 980, + 22984, + 50537, + 1161, + 19770, + 41191, + 1731, + 18584, + 35200, + 3203, + 16568, + 28531, + 5694, + 14635, + 24062, + 7150, + 12518, + 19052, + 9107, + 11804, + 15068, + 10486, + 10486, + 10486, + 14180, + 13617, + 10516, + 17026, + 16055, + 9173, + 20010, + 18761, + 8021, + 22678, + 21544, + 7183, + 25203, + 24019, + 6278, + 28328, + 27000, + 6610, + 31436, + 30119, + 6995, + 34718, + 32769, + 7223, + 11111, + 12305, + 53507, + 6689, + 13129, + 51312, + 1086, + 15733, + 42596, + 2125, + 14074, + 35254, + 5844, + 12505, + 29505, + 8441, + 11307, + 24140, + 10438, + 10147, + 19620, + 12579, + 9696, + 15771, + 14423, + 9372, + 11889, + 17554, + 11353, + 10678, + 20330, + 13777, + 9436, + 23199, + 16182, + 8558, + 25825, + 19252, + 7725, + 28046, + 22115, + 6699, + 31075, + 25190, + 6991, + 35492, + 29310, + 7713, + 37373, + 30896, + 7835, + 11134, + 12288, + 53513, + 11075, + 12273, + 53472, + 5907, + 10735, + 45621, + 6261, + 10116, + 36851, + 9198, + 9723, + 30510, + 11548, + 8966, + 25047, + 14015, + 7673, + 20316, + 15924, + 7494, + 16313, + 17913, + 7168, + 12626, + 20917, + 9822, + 10415, + 23457, + 10778, + 9624, + 25762, + 13534, + 8596, + 28584, + 16420, + 7856, + 30442, + 19050, + 6935, + 34294, + 22522, + 7287, + 37630, + 25876, + 8151, + 38622, + 28768, + 8029, + 11157, + 12273, + 53519, + 11120, + 12243, + 53484, + 8082, + 9453, + 48700, + 8928, + 7858, + 39728, + 13446, + 7845, + 32332, + 16907, + 8050, + 27894, + 19540, + 7989, + 23817, + 21147, + 7371, + 20001, + 22421, + 6797, + 15515, + 24133, + 7449, + 11769, + 25941, + 8974, + 9467, + 28637, + 9645, + 8610, + 30604, + 12149, + 7670, + 34353, + 17158, + 7044, + 36888, + 18603, + 7453, + 38633, + 21752, + 7843, + 41035, + 24969, + 8171, + 11179, + 12258, + 53526, + 11165, + 12212, + 53498, + 10005, + 8360, + 50850, + 17132, + 7139, + 42261, + 20609, + 7896, + 35837, + 23416, + 8247, + 31636, + 24667, + 8134, + 26884, + 26137, + 7202, + 23147, + 26821, + 6302, + 18669, + 28399, + 6881, + 14596, + 29613, + 7930, + 10436, + 31700, + 8318, + 8958, + 33789, + 8695, + 8193, + 35658, + 10934, + 6289, + 38227, + 16308, + 6759, + 39748, + 19538, + 7671, + 42473, + 21802, + 7776, + 11202, + 12242, + 53532, + 11210, + 12181, + 53510, + 19793, + 5213, + 50862, + 24437, + 6994, + 43733, + 28289, + 7746, + 39506, + 29287, + 8148, + 34556, + 29964, + 7793, + 30887, + 31018, + 6867, + 26528, + 31796, + 6376, + 22110, + 32866, + 6462, + 17923, + 33902, + 6884, + 14239, + 34798, + 7808, + 9693, + 37236, + 8320, + 8955, + 38852, + 8464, + 8148, + 41220, + 8900, + 7793, + 43161, + 16616, + 6943, + 44111, + 19150, + 7284, + 11383, + 12003, + 53368, + 11418, + 11929, + 53353, + 30666, + 5716, + 51801, + 33749, + 5991, + 49228, + 34284, + 7025, + 42756, + 34979, + 7381, + 39183, + 35811, + 7196, + 34848, + 36976, + 6812, + 30447, + 37527, + 7125, + 25560, + 38242, + 7036, + 21605, + 38805, + 7029, + 17616, + 39928, + 7337, + 13819, + 39620, + 8000, + 9514, + 41594, + 7873, + 8600, + 44600, + 8156, + 8027, + 49588, + 9457, + 8672, + 50103, + 10090, + 8989, + 11714, + 14060, + 51743, + 33088, + 7359, + 53770, + 36899, + 7066, + 52474, + 39631, + 6791, + 50480, + 39468, + 7308, + 46091, + 40921, + 7556, + 43887, + 41452, + 7432, + 39512, + 40902, + 7171, + 33437, + 40576, + 7289, + 27540, + 41176, + 7508, + 23442, + 41718, + 7215, + 19179, + 41725, + 7054, + 15254, + 42733, + 6957, + 9680, + 48323, + 6951, + 8641, + 49957, + 8734, + 9209, + 50390, + 9525, + 9398, + 50660, + 10026, + 9518, + 34836, + 8118, + 54503, + 38751, + 8103, + 53718, + 40837, + 7508, + 52655, + 41677, + 7002, + 51433, + 45743, + 7358, + 50810, + 48414, + 7459, + 49728, + 45648, + 7698, + 42456, + 46413, + 7437, + 38409, + 48828, + 7170, + 33266, + 45028, + 7451, + 25683, + 44819, + 7229, + 21341, + 44587, + 6846, + 16793, + 46066, + 5361, + 12066, + 50316, + 8040, + 9775, + 50673, + 8974, + 9827, + 50893, + 9571, + 9862, + 51042, + 9983, + 9888, + 7097, + 42067, + 44168, + 8015, + 41454, + 42391, + 8232, + 41125, + 41682, + 8769, + 40748, + 40254, + 9542, + 39991, + 38423, + 9252, + 40320, + 36987, + 9943, + 39885, + 35739, + 9885, + 39575, + 35076, + 10286, + 36697, + 30031, + 10461, + 36165, + 26462, + 10405, + 36080, + 25869, + 10343, + 35994, + 25266, + 8426, + 35038, + 19265, + 8442, + 35285, + 10242, + 8586, + 35359, + 9455, + 8077, + 35066, + 7991, + 7711, + 36821, + 6852, + 6281, + 41754, + 47559, + 6776, + 41303, + 43891, + 8133, + 40632, + 41533, + 8402, + 40183, + 40624, + 9168, + 39713, + 38637, + 9588, + 39020, + 37099, + 9242, + 39387, + 35483, + 10005, + 38652, + 34349, + 10544, + 35875, + 29518, + 10181, + 35941, + 25760, + 10436, + 35654, + 25382, + 10223, + 35239, + 23463, + 8426, + 34319, + 18420, + 8620, + 34201, + 9645, + 8717, + 34802, + 8810, + 8892, + 34921, + 8093, + 9099, + 35051, + 7536, + 6030, + 41244, + 47565, + 5855, + 40731, + 46519, + 6588, + 40115, + 43092, + 8297, + 39437, + 40277, + 8643, + 38776, + 39039, + 9467, + 38219, + 36654, + 8987, + 34064, + 31217, + 10166, + 35053, + 30953, + 10421, + 34783, + 28574, + 10341, + 34576, + 25452, + 10493, + 34804, + 24448, + 8822, + 32726, + 18573, + 8939, + 31714, + 10193, + 9122, + 32463, + 9280, + 9202, + 34111, + 8194, + 9316, + 34425, + 7498, + 9523, + 34608, + 6955, + 4150, + 41056, + 51885, + 4665, + 39744, + 47112, + 5595, + 39107, + 44789, + 6656, + 37532, + 40675, + 7546, + 37381, + 38563, + 8040, + 34367, + 33663, + 9693, + 32492, + 30839, + 10069, + 32111, + 28179, + 10533, + 31636, + 25844, + 10689, + 30701, + 22188, + 10377, + 30700, + 19634, + 9327, + 28871, + 11571, + 9955, + 30181, + 9984, + 9641, + 31362, + 8740, + 9830, + 32134, + 8008, + 9854, + 33832, + 6803, + 10047, + 34087, + 6285, + 3254, + 38881, + 51991, + 4028, + 38460, + 50246, + 4416, + 35487, + 43237, + 5340, + 34696, + 39645, + 6915, + 32254, + 34824, + 7736, + 30782, + 31106, + 8747, + 29791, + 28544, + 10140, + 28757, + 25724, + 10451, + 27789, + 22199, + 10510, + 28118, + 20199, + 9908, + 26607, + 13946, + 9445, + 26525, + 9590, + 10145, + 27902, + 8882, + 10520, + 29683, + 8422, + 10480, + 31035, + 7375, + 10695, + 31932, + 6796, + 22581, + 36490, + 5437, + 2752, + 36157, + 52575, + 2798, + 36004, + 50377, + 2832, + 34412, + 45028, + 4694, + 31854, + 39076, + 6521, + 29390, + 34133, + 6765, + 27620, + 30035, + 8238, + 26435, + 26477, + 9957, + 25481, + 23207, + 10191, + 24890, + 19913, + 10377, + 23606, + 15350, + 10212, + 22994, + 10424, + 10293, + 24092, + 8788, + 10407, + 25766, + 7225, + 12868, + 27901, + 6730, + 18363, + 29969, + 6453, + 23117, + 32961, + 6202, + 28111, + 37686, + 6787, + 2444, + 33239, + 52902, + 1820, + 33166, + 50720, + 3351, + 30522, + 43960, + 3960, + 28689, + 38153, + 5799, + 25953, + 32849, + 6021, + 23874, + 28245, + 7781, + 22495, + 24216, + 9503, + 21582, + 20725, + 10101, + 20628, + 16589, + 10666, + 19662, + 12098, + 10873, + 20525, + 9634, + 12832, + 21849, + 7106, + 16205, + 24312, + 5753, + 19345, + 27052, + 6053, + 23307, + 30000, + 6365, + 26451, + 32916, + 6348, + 29678, + 35808, + 6552, + 1309, + 28664, + 53703, + 310, + 28398, + 51288, + 1868, + 27601, + 45788, + 2950, + 24239, + 37227, + 3752, + 22279, + 31293, + 4726, + 19124, + 25889, + 7262, + 18236, + 21956, + 9065, + 17114, + 18128, + 10632, + 16595, + 14346, + 11695, + 16270, + 10263, + 14040, + 18295, + 8658, + 16825, + 20613, + 7265, + 19884, + 23241, + 6128, + 23146, + 26239, + 6242, + 26422, + 29155, + 6451, + 29826, + 31956, + 6631, + 32629, + 34329, + 6635, + 3733, + 24612, + 53777, + 876, + 24218, + 51759, + 1111, + 20797, + 42665, + 1636, + 19617, + 36546, + 2425, + 17724, + 29683, + 7272, + 16344, + 25774, + 9740, + 15270, + 21757, + 11803, + 14578, + 17852, + 13431, + 13674, + 13705, + 14967, + 14146, + 10118, + 17692, + 16694, + 8917, + 20533, + 19443, + 7835, + 23263, + 22228, + 6901, + 26019, + 24864, + 6287, + 29335, + 28088, + 6715, + 32710, + 31414, + 7074, + 35283, + 33239, + 7086, + 11124, + 12315, + 53518, + 3519, + 19027, + 51984, + 1157, + 17258, + 45013, + 522, + 14188, + 36300, + 5532, + 14073, + 30136, + 9841, + 13235, + 26086, + 12933, + 12564, + 21912, + 15102, + 12159, + 18124, + 16823, + 11902, + 14294, + 18312, + 11925, + 10294, + 21160, + 14625, + 9166, + 23792, + 17223, + 8263, + 26280, + 20031, + 7434, + 28594, + 23058, + 6728, + 31999, + 26000, + 7063, + 35747, + 29565, + 7727, + 38049, + 31660, + 7843, + 11147, + 12299, + 53524, + 11100, + 12294, + 53495, + 4283, + 12307, + 47658, + 4642, + 10744, + 37896, + 9596, + 10611, + 31516, + 13572, + 10407, + 26898, + 16609, + 10162, + 22885, + 18512, + 9774, + 19069, + 20219, + 9505, + 14897, + 21425, + 9280, + 10506, + 23965, + 11309, + 9407, + 26441, + 14053, + 8352, + 28860, + 16985, + 7707, + 31233, + 20044, + 6901, + 35075, + 23484, + 7475, + 38262, + 26751, + 8118, + 39839, + 29744, + 7978, + 11168, + 12283, + 53531, + 11145, + 12263, + 53507, + 8080, + 9569, + 49662, + 9603, + 7400, + 39499, + 14222, + 7590, + 33081, + 18131, + 7883, + 27493, + 20316, + 7835, + 24102, + 22248, + 7401, + 20277, + 23254, + 6764, + 15849, + 24888, + 7287, + 12181, + 26646, + 8761, + 9341, + 28982, + 10021, + 8424, + 31293, + 12764, + 7319, + 34941, + 17532, + 7135, + 38047, + 20656, + 7797, + 38886, + 22496, + 7850, + 41326, + 25816, + 8132, + 11191, + 12267, + 53537, + 11189, + 12231, + 53520, + 11368, + 7512, + 51005, + 17730, + 7036, + 43214, + 21476, + 7810, + 37014, + 24633, + 8692, + 31248, + 26152, + 8156, + 27614, + 27034, + 6979, + 23672, + 27715, + 6228, + 19061, + 29202, + 6820, + 15167, + 30343, + 7415, + 11102, + 31949, + 8259, + 8989, + 34361, + 8771, + 8033, + 36688, + 11898, + 6376, + 38607, + 17093, + 6816, + 40928, + 20670, + 7666, + 43654, + 21879, + 7563, + 11214, + 12252, + 53543, + 11395, + 11978, + 53364, + 21434, + 4945, + 52391, + 27027, + 5619, + 47076, + 29150, + 7707, + 39921, + 30531, + 8388, + 34840, + 31735, + 8089, + 30864, + 31724, + 6564, + 26776, + 32876, + 6430, + 22440, + 33465, + 6625, + 18430, + 34859, + 6823, + 14549, + 35781, + 7592, + 10128, + 38144, + 8250, + 8887, + 39753, + 8472, + 7983, + 42392, + 9717, + 7778, + 43764, + 17084, + 6950, + 44383, + 19999, + 7248, + 10999, + 13106, + 53279, + 28139, + 7365, + 54849, + 32125, + 6554, + 52754, + 35127, + 6118, + 50074, + 34849, + 7065, + 43596, + 35925, + 7481, + 39640, + 37762, + 7479, + 36464, + 37601, + 6894, + 30590, + 38475, + 7308, + 26091, + 38586, + 7057, + 21719, + 39628, + 7061, + 17810, + 40805, + 7152, + 13855, + 40930, + 7738, + 9491, + 43248, + 7713, + 8436, + 49101, + 8721, + 8407, + 49911, + 9756, + 8894, + 50351, + 10322, + 9160, + 31547, + 8149, + 55421, + 34211, + 7878, + 54289, + 38253, + 7635, + 53096, + 40765, + 6875, + 51261, + 43635, + 7168, + 49799, + 41647, + 7643, + 44094, + 42570, + 7504, + 39820, + 43230, + 7158, + 34969, + 42427, + 7327, + 28548, + 42370, + 7508, + 24119, + 42140, + 7346, + 19569, + 43804, + 6784, + 16220, + 44545, + 6268, + 10163, + 49613, + 7721, + 9183, + 50280, + 9031, + 9432, + 50639, + 9755, + 9569, + 50862, + 10213, + 9658, + 36100, + 8512, + 54789, + 39229, + 8453, + 54161, + 41936, + 7888, + 53216, + 42917, + 6867, + 52394, + 46261, + 7383, + 51322, + 49546, + 7546, + 50159, + 50029, + 7805, + 45819, + 49949, + 7612, + 40444, + 49634, + 7215, + 33682, + 49155, + 7211, + 28736, + 45253, + 7255, + 21520, + 45044, + 6852, + 16979, + 48415, + 4505, + 10038, + 50643, + 8335, + 10000, + 50923, + 9204, + 9999, + 51096, + 9758, + 10002, + 51213, + 10141, + 10006, + 7223, + 42468, + 44701, + 7955, + 41904, + 42893, + 8156, + 41581, + 42174, + 8702, + 41198, + 40713, + 9505, + 40390, + 38810, + 9229, + 40677, + 37335, + 9907, + 40218, + 36060, + 9846, + 39908, + 35361, + 10328, + 37302, + 30482, + 10982, + 35581, + 26464, + 10383, + 36266, + 26027, + 8911, + 36143, + 24545, + 9006, + 39523, + 23637, + 8419, + 35593, + 10386, + 7838, + 35496, + 8825, + 7502, + 37067, + 7628, + 7616, + 37183, + 6730, + 6474, + 42223, + 48080, + 6941, + 41817, + 44545, + 8042, + 41115, + 42083, + 8325, + 40666, + 41147, + 9114, + 40164, + 39092, + 9552, + 39417, + 37485, + 9221, + 39719, + 35810, + 9974, + 38947, + 34612, + 10434, + 36235, + 29738, + 10493, + 35914, + 26280, + 10417, + 35800, + 25488, + 10149, + 35323, + 23161, + 8363, + 34866, + 18143, + 8521, + 34977, + 9776, + 8713, + 35097, + 8845, + 8375, + 34463, + 7284, + 8056, + 36317, + 5943, + 6251, + 41774, + 48158, + 6105, + 41341, + 47207, + 6479, + 40659, + 43857, + 8202, + 39934, + 40858, + 8576, + 39257, + 39555, + 9432, + 38626, + 37065, + 10104, + 36515, + 34021, + 10145, + 35285, + 31256, + 10391, + 35150, + 28968, + 10313, + 35004, + 25865, + 10463, + 35154, + 24735, + 8477, + 33252, + 18456, + 8748, + 32467, + 10261, + 9042, + 33140, + 9219, + 9056, + 34431, + 8063, + 9318, + 34651, + 7345, + 9544, + 34832, + 6766, + 4205, + 41623, + 52583, + 4688, + 40436, + 47865, + 5625, + 39778, + 45608, + 5993, + 38892, + 42646, + 7458, + 38141, + 39383, + 9014, + 36725, + 36526, + 8942, + 34119, + 31630, + 10092, + 33202, + 29598, + 10504, + 32311, + 26593, + 10665, + 31392, + 22887, + 10282, + 31085, + 19569, + 9263, + 29348, + 11680, + 9421, + 30090, + 9727, + 9576, + 32013, + 8550, + 9869, + 33011, + 7578, + 9923, + 34017, + 6557, + 10173, + 34334, + 6131, + 3630, + 39440, + 52706, + 4095, + 39394, + 51058, + 4641, + 37987, + 46407, + 5138, + 35296, + 40499, + 7177, + 33976, + 37390, + 7724, + 32103, + 32605, + 8669, + 30645, + 29698, + 9995, + 29961, + 27135, + 10419, + 29111, + 23830, + 10689, + 28380, + 20445, + 9816, + 27243, + 14500, + 9521, + 27015, + 9727, + 10244, + 28502, + 8827, + 10442, + 29656, + 7916, + 10594, + 31598, + 6958, + 11752, + 33178, + 6806, + 23289, + 36924, + 5456, + 3779, + 36886, + 52980, + 2974, + 36941, + 51234, + 2784, + 35180, + 45925, + 5099, + 32515, + 39953, + 6547, + 30369, + 35065, + 6736, + 28751, + 31320, + 7951, + 27282, + 27360, + 9862, + 26000, + 23817, + 10134, + 25442, + 20388, + 10172, + 24239, + 15743, + 10288, + 23735, + 11004, + 10185, + 24402, + 8124, + 11303, + 26236, + 6753, + 14024, + 28590, + 6426, + 19900, + 31275, + 6298, + 24011, + 32969, + 5886, + 28331, + 37940, + 6824, + 3194, + 34305, + 53667, + 2237, + 33847, + 51657, + 2395, + 32621, + 46985, + 3746, + 29877, + 39590, + 5243, + 27245, + 33785, + 6104, + 24859, + 29257, + 7765, + 23442, + 25256, + 9514, + 22606, + 21484, + 10277, + 21408, + 17424, + 10639, + 20618, + 12860, + 11149, + 20443, + 8171, + 13585, + 22396, + 6668, + 16877, + 25017, + 5772, + 19725, + 27613, + 6059, + 24093, + 30803, + 6396, + 26985, + 33366, + 6367, + 31060, + 37301, + 6761, + 2180, + 29466, + 54594, + 400, + 29647, + 52516, + 1845, + 28653, + 46941, + 2666, + 26062, + 39795, + 3661, + 23562, + 32843, + 7139, + 21545, + 28263, + 9807, + 20598, + 24509, + 11514, + 19727, + 20678, + 12897, + 18955, + 16643, + 13785, + 18569, + 12338, + 14703, + 18793, + 8103, + 17605, + 21364, + 6928, + 20566, + 23946, + 5800, + 23973, + 27125, + 6281, + 27200, + 30027, + 6500, + 30223, + 32535, + 6635, + 33550, + 35627, + 6844, + 4338, + 25878, + 54699, + 2096, + 25992, + 52887, + 461, + 24582, + 47747, + 1792, + 21560, + 39109, + 5910, + 20016, + 32522, + 9410, + 18921, + 28122, + 12307, + 17785, + 24085, + 14264, + 16907, + 20199, + 16007, + 16264, + 16293, + 17211, + 16719, + 12322, + 18328, + 17272, + 8406, + 21096, + 20124, + 7561, + 23834, + 22937, + 6592, + 26818, + 25924, + 6479, + 30160, + 28957, + 6770, + 33282, + 31807, + 7120, + 35958, + 34201, + 7192, + 11136, + 12325, + 53530, + 4449, + 20971, + 53341, + 864, + 19491, + 48569, + 3650, + 16893, + 38898, + 8965, + 16684, + 32920, + 12840, + 15897, + 28215, + 15496, + 15025, + 24115, + 17645, + 14513, + 20358, + 19230, + 14262, + 16422, + 20590, + 14441, + 12516, + 21727, + 15042, + 8811, + 24312, + 17958, + 8039, + 26776, + 20885, + 7160, + 29453, + 23881, + 6825, + 32984, + 27112, + 7305, + 36403, + 30301, + 7776, + 38586, + 32385, + 7835, + 12721, + 11921, + 53361, + 11125, + 12313, + 53518, + 2368, + 13699, + 48833, + 7504, + 13547, + 41215, + 12796, + 13256, + 33531, + 16801, + 12976, + 28807, + 19634, + 12598, + 24982, + 21081, + 12210, + 21459, + 22358, + 11874, + 17208, + 23634, + 11700, + 12919, + 24737, + 12048, + 8965, + 27030, + 14678, + 8158, + 29597, + 17867, + 7310, + 32279, + 21136, + 7034, + 36104, + 25163, + 8017, + 38605, + 28101, + 8270, + 40596, + 29930, + 7966, + 11182, + 12292, + 53541, + 11170, + 12283, + 53531, + 8601, + 9581, + 50210, + 13116, + 9748, + 43203, + 17509, + 10270, + 36023, + 20723, + 10503, + 30767, + 22870, + 10296, + 26571, + 24365, + 9729, + 22567, + 25561, + 9147, + 18322, + 26462, + 8546, + 13585, + 27302, + 8259, + 9209, + 29517, + 10619, + 8088, + 31980, + 13594, + 7032, + 35233, + 17859, + 7184, + 38338, + 21480, + 7831, + 40403, + 24364, + 8278, + 43170, + 27765, + 7589, + 11204, + 12278, + 53549, + 11214, + 12252, + 53543, + 14633, + 5684, + 52092, + 18784, + 6680, + 45034, + 23058, + 7637, + 37371, + 25311, + 8379, + 32461, + 26987, + 8103, + 28156, + 27654, + 7300, + 23853, + 28852, + 6719, + 19704, + 29997, + 6764, + 15524, + 31414, + 7508, + 11944, + 32727, + 8276, + 8999, + 34834, + 9274, + 7750, + 37470, + 13136, + 6517, + 39408, + 17629, + 6863, + 42587, + 21457, + 7722, + 44034, + 22935, + 7582, + 11139, + 12708, + 53223, + 11419, + 11997, + 53387, + 25496, + 6407, + 53879, + 28937, + 5220, + 48698, + 30331, + 6890, + 41471, + 32098, + 7378, + 36732, + 32608, + 7099, + 31860, + 32880, + 6414, + 27285, + 33773, + 6469, + 23007, + 34496, + 6715, + 18884, + 35738, + 6760, + 14905, + 36652, + 7394, + 10521, + 38896, + 8205, + 8822, + 41073, + 8615, + 7899, + 43732, + 10889, + 7694, + 44380, + 18244, + 7345, + 44728, + 20889, + 7272, + 11408, + 12022, + 53393, + 30130, + 8072, + 55539, + 33308, + 7357, + 53708, + 36631, + 6235, + 51182, + 37460, + 6972, + 46610, + 39186, + 7823, + 42437, + 39148, + 7688, + 37432, + 39148, + 7160, + 31769, + 38991, + 7405, + 26380, + 39449, + 7203, + 22124, + 40206, + 7080, + 17812, + 41189, + 7055, + 13946, + 41928, + 7516, + 9529, + 44680, + 7481, + 8234, + 49560, + 9145, + 8723, + 50234, + 10054, + 9117, + 50599, + 10552, + 9331, + 32959, + 8624, + 55783, + 35615, + 8404, + 54745, + 38977, + 8162, + 53768, + 41617, + 7095, + 52193, + 44760, + 7303, + 50500, + 44903, + 7650, + 46943, + 44847, + 7641, + 41695, + 44884, + 7267, + 36169, + 44644, + 7327, + 30120, + 44615, + 7400, + 24912, + 44496, + 7103, + 20489, + 44344, + 6785, + 16403, + 47256, + 4968, + 10094, + 50075, + 8141, + 9503, + 50604, + 9329, + 9656, + 50887, + 9985, + 9742, + 51064, + 10400, + 9797, + 37137, + 9015, + 55343, + 39706, + 8806, + 54600, + 42594, + 8293, + 53794, + 46342, + 7374, + 53051, + 47099, + 7475, + 52025, + 50719, + 7641, + 50657, + 50440, + 7845, + 46043, + 50387, + 7637, + 40664, + 50063, + 7238, + 33834, + 49721, + 7210, + 28953, + 49637, + 6728, + 23907, + 49745, + 6393, + 19753, + 49963, + 5338, + 12368, + 50968, + 8632, + 10223, + 51172, + 9432, + 10170, + 51298, + 9945, + 10142, + 51383, + 10299, + 10124, + 7352, + 42872, + 45236, + 8051, + 42387, + 43397, + 8069, + 42096, + 42731, + 8624, + 41710, + 41236, + 9463, + 40849, + 39257, + 9200, + 41092, + 37740, + 9862, + 40615, + 36441, + 9846, + 40396, + 35771, + 10366, + 37913, + 30936, + 10440, + 38116, + 28814, + 10356, + 36515, + 26238, + 9896, + 39702, + 25605, + 9122, + 40043, + 23931, + 7582, + 35998, + 9991, + 7266, + 37382, + 8658, + 7378, + 37498, + 7544, + 7499, + 38881, + 6711, + 6668, + 42691, + 48601, + 7113, + 42333, + 45199, + 7939, + 41668, + 42712, + 8234, + 41229, + 41750, + 9036, + 40702, + 39640, + 9510, + 39892, + 37949, + 9196, + 40128, + 36209, + 9936, + 39318, + 34943, + 10343, + 36534, + 30044, + 10480, + 36141, + 26580, + 10391, + 36006, + 25633, + 8482, + 36023, + 22482, + 8295, + 35703, + 17664, + 8499, + 35346, + 9915, + 8103, + 34902, + 8149, + 7770, + 36742, + 6671, + 8115, + 37023, + 5962, + 6474, + 42303, + 48751, + 6357, + 41955, + 47893, + 6720, + 41372, + 44722, + 8090, + 40527, + 41547, + 8491, + 39839, + 40178, + 9386, + 39131, + 37574, + 9478, + 38428, + 35941, + 10111, + 35619, + 31691, + 10841, + 34726, + 29257, + 10295, + 35294, + 26051, + 10440, + 35249, + 24731, + 8421, + 33697, + 18601, + 8620, + 32778, + 10440, + 8793, + 34317, + 8999, + 9020, + 34718, + 7943, + 9316, + 34929, + 7156, + 8617, + 35528, + 4671, + 4499, + 42227, + 53290, + 4792, + 41199, + 48684, + 5766, + 40564, + 46538, + 5853, + 39601, + 43577, + 7392, + 38753, + 40067, + 8970, + 37302, + 37143, + 9031, + 34853, + 32383, + 9770, + 32863, + 29408, + 10478, + 33070, + 27422, + 10782, + 32532, + 24271, + 10189, + 31511, + 19579, + 8512, + 30179, + 13349, + 9276, + 30957, + 9708, + 9590, + 32235, + 8409, + 9733, + 33904, + 6994, + 10010, + 34249, + 6251, + 17481, + 37097, + 4496, + 4950, + 39970, + 53195, + 4161, + 40400, + 51965, + 4695, + 38858, + 47407, + 4669, + 38002, + 44303, + 7095, + 35222, + 38803, + 7597, + 33380, + 34125, + 8546, + 31535, + 30012, + 9921, + 30592, + 27863, + 10412, + 29749, + 24464, + 10670, + 28810, + 20615, + 9738, + 27860, + 15093, + 9415, + 27487, + 9919, + 10371, + 29078, + 8690, + 10592, + 30400, + 7428, + 11746, + 31934, + 7181, + 20942, + 36013, + 5309, + 24017, + 37398, + 5479, + 4363, + 37574, + 53718, + 3237, + 37954, + 52204, + 2815, + 36213, + 47241, + 4416, + 34602, + 42830, + 5500, + 32451, + 37633, + 7232, + 29825, + 32138, + 7746, + 28073, + 28213, + 9737, + 27253, + 25274, + 10464, + 26266, + 21282, + 10216, + 25089, + 16536, + 9588, + 24144, + 11037, + 10151, + 24057, + 6137, + 12973, + 26850, + 6602, + 17403, + 29349, + 6607, + 21457, + 32593, + 6226, + 26413, + 35165, + 5893, + 28584, + 38230, + 6865, + 3912, + 35211, + 54440, + 2900, + 35017, + 52782, + 2092, + 34505, + 49842, + 3853, + 31253, + 41643, + 5253, + 28847, + 35526, + 7378, + 26717, + 31376, + 9754, + 25520, + 27519, + 11486, + 24570, + 23869, + 12404, + 23637, + 19907, + 12773, + 22818, + 15148, + 13260, + 22655, + 10531, + 13929, + 22683, + 5882, + 17905, + 25600, + 5948, + 21532, + 28725, + 6320, + 24926, + 31601, + 6367, + 28130, + 34756, + 6372, + 31929, + 38270, + 6863, + 3021, + 30656, + 55498, + 1584, + 31043, + 53781, + 1556, + 30990, + 49844, + 2593, + 27532, + 41800, + 5270, + 25655, + 35673, + 9335, + 24019, + 30864, + 11879, + 22809, + 26893, + 13758, + 21818, + 22877, + 14933, + 21067, + 18887, + 15984, + 20835, + 14572, + 16966, + 21286, + 10559, + 18000, + 22020, + 6445, + 21282, + 24718, + 5695, + 24653, + 27892, + 6342, + 28120, + 31012, + 6567, + 31672, + 33902, + 6647, + 34823, + 37346, + 7217, + 5097, + 26837, + 55544, + 3193, + 27375, + 54222, + 355, + 26365, + 50240, + 4141, + 23983, + 41803, + 8688, + 22420, + 35118, + 12151, + 21316, + 30550, + 14653, + 20102, + 26405, + 16703, + 19253, + 22577, + 18392, + 18643, + 18679, + 19548, + 19249, + 14671, + 20584, + 19771, + 10909, + 21737, + 20826, + 7249, + 24400, + 23705, + 6458, + 27677, + 26808, + 6546, + 30786, + 29881, + 6933, + 34187, + 32832, + 7156, + 36685, + 35229, + 7411, + 11149, + 12335, + 53541, + 5496, + 22491, + 54583, + 784, + 21218, + 51143, + 6766, + 20210, + 42830, + 11849, + 19286, + 35969, + 15692, + 18299, + 30606, + 18238, + 17523, + 26458, + 20233, + 17063, + 22756, + 21670, + 16804, + 18841, + 22897, + 16930, + 14807, + 23882, + 17547, + 11298, + 25079, + 18864, + 7917, + 27409, + 21674, + 6907, + 30607, + 24980, + 7055, + 34351, + 28622, + 7599, + 37071, + 31056, + 7783, + 38883, + 32797, + 7572, + 11170, + 12319, + 53547, + 11149, + 12334, + 53541, + 5403, + 16178, + 51904, + 10775, + 16304, + 44300, + 16040, + 15901, + 35633, + 19848, + 15456, + 30987, + 22316, + 15026, + 27183, + 23575, + 14703, + 23692, + 24872, + 14159, + 19529, + 25884, + 14133, + 15254, + 26820, + 14484, + 11434, + 27808, + 15406, + 8035, + 30007, + 18657, + 7094, + 33564, + 22288, + 7207, + 36973, + 25945, + 8127, + 38871, + 28723, + 8029, + 41527, + 30556, + 7896, + 11193, + 12303, + 53554, + 11193, + 12303, + 53554, + 12110, + 12110, + 51630, + 16186, + 12146, + 45967, + 21260, + 12960, + 37422, + 24093, + 13051, + 32433, + 25614, + 12687, + 28611, + 26827, + 12188, + 24773, + 27883, + 11551, + 20405, + 28753, + 11124, + 15915, + 29546, + 10820, + 11527, + 30400, + 11461, + 7787, + 32419, + 14574, + 6970, + 35968, + 18360, + 7326, + 38702, + 21778, + 7833, + 41259, + 25146, + 8149, + 44000, + 28618, + 7500, + 11216, + 12288, + 53560, + 11397, + 12047, + 53398, + 17786, + 7797, + 55688, + 22659, + 8833, + 48303, + 26105, + 9993, + 40668, + 28331, + 10564, + 34893, + 29356, + 10153, + 30656, + 30362, + 9564, + 26370, + 31183, + 9012, + 21758, + 31625, + 8070, + 17068, + 32001, + 7108, + 12254, + 33514, + 8290, + 9024, + 35213, + 9841, + 6987, + 38016, + 13965, + 6574, + 40756, + 18645, + 7453, + 43754, + 21554, + 7508, + 44350, + 23978, + 7606, + 11397, + 12048, + 53398, + 11442, + 12018, + 53412, + 27783, + 7677, + 55383, + 29659, + 5895, + 50419, + 32600, + 7487, + 42168, + 34061, + 8459, + 36304, + 33494, + 7500, + 32862, + 34214, + 6856, + 28550, + 34848, + 6563, + 23557, + 35568, + 6787, + 19407, + 36340, + 7228, + 15690, + 37698, + 7601, + 11303, + 39369, + 8253, + 8858, + 42226, + 8978, + 7911, + 45100, + 12420, + 7202, + 45133, + 18112, + 7304, + 45110, + 21278, + 7241, + 11419, + 12033, + 53404, + 31725, + 8632, + 56122, + 35024, + 8190, + 54566, + 38186, + 7268, + 52543, + 40362, + 6747, + 49001, + 40404, + 7493, + 43659, + 39926, + 7804, + 37928, + 40246, + 7023, + 32366, + 39951, + 7563, + 26913, + 40689, + 7441, + 22663, + 41435, + 7114, + 18337, + 41754, + 6911, + 14079, + 43412, + 6882, + 9360, + 48108, + 7732, + 8369, + 50017, + 9567, + 9039, + 50554, + 10354, + 9339, + 50847, + 10782, + 9501, + 34345, + 9086, + 56123, + 36985, + 8920, + 55189, + 39698, + 8694, + 54435, + 42515, + 7797, + 53157, + 46024, + 7321, + 51698, + 47631, + 7398, + 49605, + 45876, + 7692, + 42480, + 46680, + 7390, + 37606, + 46182, + 7400, + 31042, + 45340, + 7456, + 25391, + 45134, + 7147, + 20771, + 45022, + 6787, + 16639, + 48114, + 4653, + 9949, + 50535, + 8561, + 9820, + 50926, + 9626, + 9877, + 51135, + 10215, + 9913, + 51265, + 10588, + 9936, + 37941, + 9351, + 55736, + 40181, + 9162, + 55038, + 43130, + 8723, + 54353, + 47639, + 7525, + 53967, + 48403, + 7559, + 52764, + 50638, + 8065, + 50316, + 50951, + 7897, + 46353, + 50936, + 7670, + 40967, + 50596, + 7265, + 34051, + 50256, + 7207, + 29143, + 50195, + 6708, + 24112, + 50459, + 6920, + 20170, + 50654, + 5928, + 12908, + 51291, + 8928, + 10448, + 51421, + 9662, + 10343, + 51607, + 10775, + 10559, + 51668, + 10672, + 10240, + 7485, + 43277, + 45774, + 8147, + 42873, + 43901, + 8167, + 42650, + 43305, + 8531, + 42299, + 41838, + 9411, + 41385, + 39776, + 9166, + 41585, + 38217, + 9858, + 41149, + 36920, + 9910, + 41037, + 36296, + 10400, + 38531, + 31393, + 10584, + 39598, + 29969, + 11007, + 37177, + 27222, + 9385, + 40704, + 25152, + 9234, + 40567, + 24227, + 7027, + 37810, + 10071, + 7099, + 37907, + 8648, + 7346, + 40001, + 7546, + 8227, + 41175, + 7439, + 6863, + 43161, + 49120, + 7287, + 42853, + 45857, + 8019, + 42299, + 43381, + 8126, + 41887, + 42459, + 8938, + 41343, + 40294, + 9457, + 40472, + 38511, + 9162, + 40639, + 36707, + 9884, + 39801, + 35371, + 10318, + 37246, + 30628, + 10371, + 37355, + 28506, + 10351, + 36312, + 25852, + 8869, + 38327, + 23577, + 8196, + 36907, + 17563, + 7788, + 35445, + 9478, + 7473, + 37089, + 7778, + 7761, + 37312, + 6629, + 8324, + 39285, + 6334, + 6700, + 42832, + 49343, + 6611, + 42569, + 48576, + 6628, + 42236, + 46281, + 7206, + 41596, + 42856, + 8382, + 40557, + 40944, + 9320, + 39775, + 38221, + 9447, + 38945, + 36450, + 10119, + 37610, + 33444, + 10744, + 35165, + 29548, + 10270, + 35722, + 26540, + 10388, + 35373, + 24597, + 8428, + 34220, + 18837, + 8418, + 33588, + 10737, + 8662, + 34893, + 9063, + 8972, + 35087, + 7788, + 8337, + 35922, + 5278, + 8869, + 36806, + 4954, + 4901, + 42832, + 53984, + 5192, + 42001, + 49477, + 6133, + 41448, + 47534, + 5960, + 40569, + 44762, + 7253, + 39637, + 41138, + 8907, + 37912, + 37791, + 8974, + 36236, + 34315, + 9617, + 33385, + 29936, + 10457, + 33562, + 27939, + 10779, + 32927, + 24636, + 10193, + 32090, + 19970, + 8354, + 31145, + 14383, + 9079, + 31847, + 9719, + 9592, + 33098, + 8048, + 9813, + 34196, + 6654, + 10187, + 34620, + 6028, + 17689, + 37692, + 4408, + 5351, + 40904, + 53944, + 4372, + 41485, + 52959, + 4752, + 39850, + 48561, + 4589, + 39025, + 45511, + 7033, + 36241, + 39867, + 7371, + 34505, + 35783, + 8679, + 32018, + 31073, + 9923, + 31101, + 28225, + 10407, + 30463, + 25213, + 10352, + 29517, + 21071, + 9596, + 28728, + 15858, + 9457, + 28282, + 10793, + 10569, + 29595, + 8359, + 11739, + 31242, + 7101, + 12714, + 32995, + 6570, + 21585, + 36572, + 5304, + 24768, + 37917, + 5502, + 4932, + 38479, + 54420, + 3982, + 39016, + 53195, + 4089, + 38304, + 50715, + 4312, + 35693, + 44350, + 5346, + 33675, + 39122, + 7613, + 31444, + 34053, + 10052, + 30210, + 30645, + 11334, + 29120, + 27164, + 12166, + 28130, + 23512, + 12205, + 27137, + 18965, + 12069, + 26574, + 13864, + 12425, + 26414, + 8763, + 14515, + 27728, + 6323, + 18019, + 30176, + 6453, + 21965, + 33045, + 6185, + 27445, + 36572, + 5964, + 28513, + 38826, + 6338, + 4800, + 35736, + 55068, + 3824, + 36205, + 53896, + 2355, + 35838, + 51316, + 2946, + 33363, + 44810, + 6747, + 30556, + 38036, + 9533, + 28934, + 33496, + 11539, + 27648, + 29835, + 13169, + 26462, + 25832, + 14222, + 25462, + 22086, + 14534, + 24741, + 17537, + 15276, + 24695, + 12739, + 16221, + 24965, + 8295, + 18541, + 26623, + 6069, + 22479, + 29738, + 6388, + 25759, + 32752, + 6299, + 29171, + 35854, + 6509, + 32402, + 38578, + 6922, + 3963, + 31839, + 56357, + 3809, + 33007, + 54790, + 1809, + 32554, + 51642, + 3311, + 29497, + 44521, + 9060, + 27835, + 38021, + 11625, + 26289, + 33463, + 13977, + 24884, + 29206, + 15739, + 23857, + 25106, + 17014, + 23145, + 21145, + 18219, + 23035, + 16915, + 19271, + 23462, + 12813, + 20270, + 24196, + 8810, + 22024, + 25540, + 5865, + 25911, + 28875, + 6496, + 29090, + 31838, + 6618, + 32227, + 34556, + 6627, + 35365, + 38068, + 7301, + 5953, + 28088, + 56205, + 4346, + 28787, + 55594, + 0, + 28546, + 53619, + 5726, + 26949, + 45538, + 10914, + 24871, + 38729, + 14469, + 23403, + 33095, + 17057, + 22281, + 28828, + 19113, + 21524, + 24916, + 20744, + 20995, + 21037, + 21957, + 21488, + 17107, + 22982, + 22258, + 13211, + 24064, + 23334, + 9645, + 25026, + 24556, + 6285, + 28525, + 27719, + 6616, + 31853, + 30990, + 6996, + 34832, + 33351, + 7021, + 38472, + 36989, + 7793, + 7521, + 23433, + 56171, + 6593, + 24030, + 55852, + 3774, + 24710, + 54315, + 9607, + 22925, + 46046, + 14610, + 21670, + 38148, + 18398, + 20598, + 33125, + 20862, + 19860, + 28937, + 22726, + 19453, + 25102, + 24106, + 19271, + 21155, + 25266, + 19309, + 17187, + 26285, + 20180, + 13547, + 27209, + 21071, + 10048, + 28063, + 22638, + 6922, + 31269, + 25640, + 6991, + 35131, + 29406, + 7652, + 37770, + 31851, + 7801, + 40211, + 34089, + 7879, + 11184, + 12328, + 53558, + 10991, + 12756, + 53701, + 8378, + 19132, + 55636, + 13564, + 19234, + 46700, + 19354, + 18476, + 38241, + 22701, + 17926, + 33436, + 24777, + 17548, + 29561, + 25939, + 17241, + 25990, + 27135, + 16747, + 21693, + 28224, + 16612, + 17544, + 29221, + 17236, + 13701, + 30021, + 18065, + 10253, + 30887, + 19630, + 6831, + 34203, + 23098, + 7383, + 37734, + 26759, + 8105, + 40039, + 29731, + 7977, + 42672, + 31642, + 8067, + 11206, + 12313, + 53564, + 11219, + 12323, + 53577, + 14674, + 14096, + 57540, + 19635, + 14371, + 48793, + 24541, + 15272, + 39607, + 26962, + 15352, + 34576, + 28063, + 15044, + 30718, + 29072, + 14591, + 26958, + 30198, + 13954, + 22560, + 31031, + 13479, + 18103, + 31795, + 13327, + 13726, + 32588, + 14030, + 10101, + 33226, + 14920, + 6445, + 36907, + 19432, + 7498, + 39381, + 23353, + 8280, + 41710, + 26029, + 8076, + 44330, + 29377, + 7556, + 11147, + 12402, + 52651, + 11421, + 12067, + 53422, + 21314, + 9702, + 59738, + 26249, + 10734, + 51172, + 29725, + 12412, + 42119, + 31170, + 12927, + 36832, + 31744, + 12587, + 32688, + 32790, + 11839, + 28364, + 33479, + 11222, + 23743, + 34001, + 10573, + 19155, + 34585, + 9947, + 14596, + 35118, + 9657, + 10187, + 35793, + 10303, + 6257, + 39046, + 15121, + 6675, + 42290, + 19782, + 7542, + 44152, + 21902, + 7476, + 44760, + 25352, + 7557, + 11409, + 12056, + 53410, + 11467, + 12037, + 53436, + 30295, + 8936, + 56799, + 33041, + 7867, + 53343, + 35456, + 9447, + 45566, + 36382, + 10305, + 39418, + 36063, + 9640, + 35205, + 36702, + 9114, + 30598, + 36817, + 8407, + 25110, + 37145, + 7765, + 20483, + 37220, + 7177, + 15994, + 38816, + 7599, + 12109, + 40590, + 8067, + 8658, + 43801, + 9981, + 7928, + 46882, + 14072, + 7038, + 49012, + 17419, + 7128, + 50063, + 21866, + 7692, + 11432, + 12041, + 53417, + 33328, + 9295, + 56717, + 36701, + 9014, + 55401, + 40217, + 8388, + 53858, + 42134, + 6884, + 50594, + 41736, + 7651, + 44505, + 42070, + 7461, + 39415, + 41881, + 7203, + 33341, + 42132, + 7513, + 28132, + 42191, + 7502, + 23463, + 42364, + 7369, + 19050, + 43674, + 6631, + 14819, + 44950, + 6344, + 9516, + 49485, + 8396, + 8856, + 50474, + 9991, + 9354, + 50875, + 10651, + 9559, + 50883, + 17289, + 8616, + 34980, + 9491, + 56569, + 37933, + 9520, + 55985, + 40832, + 9295, + 55024, + 43412, + 8514, + 54108, + 47110, + 7367, + 52895, + 49013, + 7500, + 50394, + 48801, + 7776, + 44425, + 49191, + 7340, + 38810, + 49206, + 7166, + 32607, + 48673, + 7166, + 27462, + 48700, + 6694, + 22545, + 48270, + 5937, + 17253, + 49754, + 5083, + 10546, + 50993, + 8980, + 10138, + 51248, + 9923, + 10100, + 51383, + 10445, + 10084, + 51248, + 11731, + 10735, + 39105, + 9695, + 56011, + 40657, + 9519, + 55473, + 43670, + 9158, + 54906, + 48584, + 7883, + 55140, + 49862, + 7651, + 53711, + 51317, + 8152, + 50976, + 51609, + 7964, + 46787, + 51646, + 7713, + 41385, + 51273, + 7300, + 34359, + 50932, + 7204, + 29408, + 50972, + 7270, + 24610, + 51170, + 7449, + 20590, + 51602, + 6597, + 13389, + 51753, + 9462, + 10679, + 51792, + 10116, + 10518, + 51814, + 10535, + 10421, + 51828, + 10824, + 10355, + 7622, + 43685, + 46314, + 8242, + 43359, + 44404, + 8271, + 43204, + 43879, + 8494, + 43207, + 43095, + 9350, + 42016, + 40390, + 9124, + 42176, + 38791, + 9924, + 41793, + 37454, + 9972, + 41684, + 36827, + 10288, + 41841, + 34248, + 10668, + 41763, + 32044, + 10369, + 39677, + 28597, + 9493, + 41229, + 25454, + 9343, + 41092, + 24524, + 5409, + 38771, + 9140, + 6888, + 40775, + 8421, + 8230, + 42716, + 8354, + 9133, + 45937, + 8485, + 7061, + 43630, + 49638, + 7468, + 43375, + 46517, + 8147, + 42937, + 44043, + 8174, + 42643, + 43250, + 8814, + 42125, + 41087, + 9389, + 41195, + 39209, + 9120, + 41297, + 37346, + 9849, + 40523, + 35996, + 10381, + 38281, + 31398, + 10447, + 38307, + 29113, + 9462, + 36526, + 25977, + 9155, + 39924, + 24260, + 5753, + 38960, + 15762, + 7130, + 37592, + 9462, + 6415, + 37764, + 6909, + 8124, + 40331, + 7090, + 9065, + 41729, + 7164, + 6928, + 43363, + 49930, + 6869, + 43183, + 49255, + 6901, + 42957, + 47142, + 7289, + 42416, + 43860, + 8240, + 41466, + 41910, + 9225, + 40622, + 39067, + 9400, + 39662, + 37152, + 10067, + 38479, + 34258, + 10583, + 35787, + 30096, + 10242, + 36205, + 27106, + 9153, + 35517, + 24165, + 8425, + 35000, + 19159, + 8221, + 35260, + 12098, + 8090, + 34785, + 8506, + 7947, + 36428, + 6291, + 8577, + 37112, + 5498, + 9406, + 39637, + 5806, + 5306, + 43438, + 54678, + 4490, + 44066, + 53963, + 6502, + 42336, + 48527, + 6392, + 41710, + 46083, + 7061, + 40586, + 42368, + 8801, + 38790, + 38719, + 9877, + 36116, + 34933, + 10189, + 34264, + 30969, + 10729, + 33562, + 28113, + 10410, + 33578, + 25311, + 10073, + 32503, + 20540, + 8434, + 32094, + 15013, + 8867, + 32598, + 9845, + 9427, + 34090, + 7490, + 9929, + 34611, + 6173, + 9824, + 35340, + 4099, + 17928, + 38383, + 4298, + 5751, + 41852, + 54705, + 4946, + 42491, + 53912, + 4104, + 41879, + 52048, + 4634, + 40335, + 47052, + 5969, + 38175, + 42343, + 7504, + 35239, + 36624, + 9718, + 34083, + 33273, + 11187, + 33339, + 30421, + 11800, + 32351, + 26905, + 11997, + 31499, + 22998, + 11537, + 30782, + 18164, + 10847, + 30238, + 12743, + 11354, + 29730, + 7506, + 13050, + 31944, + 6782, + 17534, + 34256, + 4928, + 22252, + 37215, + 5295, + 27452, + 40394, + 5667, + 5498, + 39387, + 55136, + 4724, + 40086, + 54189, + 4231, + 39664, + 52167, + 4648, + 37612, + 47213, + 5475, + 35338, + 41310, + 9209, + 33640, + 36595, + 11334, + 32311, + 32722, + 12830, + 30990, + 29158, + 13596, + 29995, + 25437, + 13866, + 29155, + 21157, + 13863, + 28610, + 15920, + 14634, + 28679, + 11333, + 16390, + 28520, + 6958, + 19128, + 31298, + 6306, + 23191, + 33229, + 5747, + 28122, + 37318, + 6005, + 29069, + 39240, + 6401, + 5486, + 36758, + 55863, + 4741, + 37398, + 55002, + 3553, + 37125, + 52676, + 2611, + 35034, + 47109, + 8506, + 32754, + 40757, + 11267, + 31099, + 36233, + 13450, + 29704, + 31959, + 14699, + 28464, + 27877, + 15931, + 27498, + 24089, + 16618, + 26850, + 19757, + 17363, + 26815, + 14885, + 18776, + 27428, + 10869, + 20020, + 28143, + 7144, + 23311, + 30685, + 6431, + 26443, + 33431, + 6325, + 30704, + 37190, + 6732, + 32904, + 38935, + 6983, + 4893, + 33018, + 57211, + 4975, + 34336, + 55971, + 3296, + 34372, + 53789, + 5536, + 32269, + 47531, + 10277, + 30183, + 41149, + 13509, + 28447, + 36036, + 15850, + 27031, + 31651, + 17867, + 26013, + 27431, + 19209, + 25382, + 23414, + 20483, + 25258, + 19234, + 21575, + 25654, + 15125, + 22644, + 26489, + 11295, + 23834, + 27342, + 7603, + 26481, + 29801, + 6464, + 29744, + 32627, + 6628, + 33214, + 35665, + 6781, + 36587, + 39176, + 7461, + 6624, + 29253, + 57158, + 5553, + 30227, + 57000, + 2763, + 30921, + 56482, + 8588, + 29219, + 48228, + 13322, + 27203, + 41164, + 16897, + 25513, + 35728, + 19497, + 24428, + 31410, + 21491, + 23811, + 27316, + 23083, + 23330, + 23374, + 24234, + 23749, + 19403, + 25212, + 24471, + 15511, + 26202, + 25368, + 11795, + 27215, + 26408, + 8299, + 29352, + 28649, + 6692, + 32714, + 31834, + 7095, + 35768, + 34456, + 7152, + 39053, + 37926, + 7942, + 8143, + 24741, + 57047, + 8130, + 25694, + 56939, + 6780, + 27076, + 57262, + 12374, + 25638, + 48919, + 17403, + 23978, + 41141, + 21024, + 22843, + 35851, + 23467, + 22183, + 31478, + 25150, + 21725, + 27492, + 26396, + 21570, + 23486, + 27523, + 21634, + 19441, + 28525, + 22486, + 15718, + 29502, + 23521, + 12210, + 30270, + 24640, + 8820, + 32319, + 26880, + 7246, + 35797, + 30019, + 7582, + 38633, + 32408, + 7563, + 41725, + 35868, + 8179, + 11196, + 12339, + 53570, + 11011, + 13874, + 54214, + 11522, + 22062, + 58669, + 16990, + 21659, + 49712, + 22247, + 20758, + 41250, + 25290, + 20296, + 36127, + 27297, + 19978, + 32027, + 28350, + 19704, + 28338, + 29500, + 19297, + 23893, + 30607, + 19037, + 19702, + 31596, + 19731, + 15794, + 32459, + 20750, + 12422, + 33256, + 21980, + 8906, + 35112, + 24131, + 7525, + 38531, + 27962, + 8276, + 41313, + 30120, + 7968, + 43906, + 32702, + 8126, + 11219, + 12324, + 53577, + 11216, + 12336, + 53632, + 18679, + 16616, + 60904, + 23305, + 17072, + 51467, + 27412, + 17660, + 42210, + 29462, + 17801, + 37128, + 30537, + 17578, + 33070, + 31524, + 17270, + 29118, + 32667, + 16519, + 24561, + 33509, + 16141, + 20277, + 34308, + 16109, + 16038, + 35097, + 16905, + 12309, + 35792, + 17696, + 8656, + 37888, + 21023, + 7808, + 40846, + 24488, + 8242, + 43864, + 27666, + 7519, + 44686, + 29820, + 7593, + 11398, + 12081, + 53415, + 11444, + 12087, + 53446, + 25568, + 12199, + 63033, + 30166, + 13161, + 53777, + 32759, + 14435, + 44946, + 33617, + 15072, + 39293, + 34182, + 14930, + 34861, + 35186, + 14357, + 30369, + 35984, + 13687, + 25711, + 36570, + 13252, + 21340, + 37135, + 12611, + 16945, + 37759, + 12336, + 12422, + 38449, + 13112, + 8494, + 40508, + 15996, + 6778, + 43720, + 20600, + 7422, + 44649, + 22974, + 7475, + 46508, + 26275, + 7025, + 11421, + 12067, + 53422, + 28603, + 10488, + 59212, + 32413, + 10204, + 58141, + 37040, + 9746, + 56403, + 38635, + 11134, + 49029, + 38576, + 11661, + 43187, + 38786, + 11653, + 37641, + 39261, + 11134, + 32326, + 39588, + 10773, + 27205, + 39943, + 10246, + 22579, + 40225, + 9525, + 17981, + 40896, + 8675, + 13256, + 42100, + 7753, + 8271, + 45325, + 10541, + 7514, + 48101, + 14987, + 7126, + 49902, + 19493, + 7696, + 50518, + 23167, + 7892, + 11444, + 12052, + 53429, + 34893, + 9946, + 57278, + 38168, + 9936, + 56538, + 42001, + 9478, + 55249, + 44843, + 7126, + 53195, + 44376, + 7470, + 47168, + 43940, + 7597, + 40811, + 43993, + 7148, + 34589, + 44012, + 7455, + 29122, + 44068, + 7667, + 24427, + 44627, + 7006, + 19824, + 44926, + 6483, + 15097, + 47356, + 5061, + 9603, + 50272, + 9119, + 9398, + 50928, + 10413, + 9668, + 51195, + 10950, + 9780, + 51244, + 18818, + 8882, + 35515, + 9890, + 57029, + 39225, + 9991, + 56416, + 42060, + 9826, + 55684, + 44882, + 9196, + 55267, + 48731, + 7441, + 54680, + 50621, + 7624, + 51528, + 50467, + 7840, + 45895, + 50382, + 7376, + 39447, + 50171, + 7217, + 33096, + 49904, + 7160, + 28018, + 49953, + 6642, + 23069, + 50299, + 6528, + 18392, + 50913, + 6090, + 11421, + 51573, + 10174, + 10670, + 51687, + 10440, + 10324, + 51738, + 10885, + 10251, + 51769, + 11168, + 10206, + 40252, + 10033, + 56285, + 41902, + 9942, + 55831, + 44213, + 9599, + 55452, + 49345, + 8534, + 55874, + 52180, + 7710, + 55015, + 52225, + 8274, + 51860, + 52463, + 8070, + 47395, + 52425, + 7466, + 41348, + 52149, + 7346, + 34789, + 51793, + 7523, + 29877, + 51750, + 7861, + 25110, + 51878, + 7982, + 21005, + 52254, + 7184, + 13920, + 52056, + 9746, + 10896, + 52025, + 10337, + 10685, + 52004, + 10715, + 10556, + 51988, + 10978, + 10469, + 7762, + 44095, + 46855, + 8337, + 43846, + 44907, + 8375, + 43760, + 44452, + 8613, + 44149, + 43731, + 8713, + 44997, + 42913, + 9073, + 42901, + 39491, + 9987, + 42442, + 37992, + 10033, + 42337, + 37362, + 10360, + 42297, + 34599, + 10704, + 42110, + 32264, + 10600, + 41875, + 29889, + 9599, + 41757, + 25759, + 9390, + 41584, + 24500, + 6263, + 42009, + 9772, + 8096, + 45127, + 9352, + 9219, + 48114, + 9467, + 9769, + 48932, + 9322, + 7262, + 44099, + 50154, + 7653, + 43900, + 47179, + 8272, + 43577, + 44707, + 8316, + 43406, + 44041, + 8650, + 43638, + 42874, + 9301, + 42120, + 40099, + 9060, + 42178, + 38196, + 9953, + 41606, + 36884, + 10101, + 41660, + 34341, + 10727, + 41334, + 31411, + 10345, + 39818, + 28018, + 9347, + 40824, + 24776, + 5498, + 39953, + 15965, + 5675, + 39105, + 8591, + 7479, + 40873, + 7850, + 8877, + 43600, + 7861, + 9649, + 45665, + 8017, + 5686, + 46310, + 55070, + 7129, + 43796, + 49934, + 7179, + 43680, + 47998, + 7569, + 43271, + 44853, + 8189, + 42627, + 43130, + 9077, + 41788, + 40223, + 9327, + 40727, + 38190, + 9379, + 39542, + 35106, + 10740, + 36815, + 31318, + 11037, + 36000, + 28394, + 10396, + 35057, + 24786, + 8310, + 36662, + 19659, + 7485, + 35971, + 12370, + 7395, + 37147, + 8205, + 7660, + 38027, + 6040, + 9145, + 40229, + 6301, + 10147, + 42241, + 6760, + 5711, + 44043, + 55369, + 5091, + 45110, + 54751, + 6878, + 43225, + 49514, + 6833, + 42855, + 47398, + 7065, + 41961, + 44026, + 8615, + 40157, + 40154, + 9647, + 37711, + 35625, + 10706, + 36880, + 33332, + 11609, + 36315, + 30485, + 11706, + 35561, + 26722, + 11478, + 34749, + 22435, + 9959, + 34258, + 16931, + 8497, + 33760, + 10258, + 9470, + 34758, + 6959, + 9524, + 35520, + 4093, + 16903, + 38344, + 4199, + 21921, + 41069, + 4722, + 6148, + 42813, + 55478, + 5511, + 43324, + 54869, + 4180, + 43480, + 53843, + 5193, + 41940, + 48831, + 7498, + 38684, + 43226, + 9451, + 37698, + 39231, + 10659, + 36707, + 35927, + 12353, + 35335, + 32456, + 13125, + 34235, + 28961, + 13207, + 33507, + 24971, + 13043, + 32843, + 20181, + 12423, + 32276, + 14542, + 13327, + 32317, + 9407, + 14487, + 33148, + 5952, + 18156, + 36143, + 5055, + 22747, + 37970, + 5236, + 27651, + 41305, + 5509, + 6067, + 40296, + 55870, + 5470, + 41025, + 55184, + 5084, + 41170, + 53714, + 4002, + 40056, + 50268, + 8070, + 37437, + 44002, + 10562, + 35872, + 39240, + 12746, + 34442, + 34812, + 13931, + 33132, + 31187, + 14650, + 31923, + 27359, + 15283, + 31189, + 23052, + 15625, + 30655, + 17943, + 16648, + 30758, + 13201, + 18061, + 31164, + 8903, + 20067, + 32379, + 6209, + 24104, + 34486, + 5654, + 28466, + 38502, + 6874, + 30683, + 40272, + 6893, + 6173, + 37770, + 56663, + 5654, + 38592, + 56100, + 4913, + 38672, + 54395, + 4638, + 37367, + 50067, + 9605, + 35079, + 44026, + 12878, + 33252, + 38734, + 14823, + 31904, + 34182, + 16588, + 30700, + 30104, + 17818, + 29789, + 26122, + 18782, + 29185, + 21984, + 19672, + 29025, + 17314, + 21189, + 29694, + 13218, + 22472, + 30362, + 9292, + 24239, + 31666, + 6457, + 27776, + 34836, + 6332, + 31338, + 38267, + 6780, + 33441, + 39350, + 7047, + 6493, + 34718, + 57743, + 6131, + 35663, + 57127, + 5179, + 36163, + 55897, + 7722, + 34517, + 50550, + 12343, + 32332, + 43853, + 15470, + 30606, + 38690, + 18092, + 29221, + 34182, + 20014, + 28325, + 29833, + 21442, + 27705, + 25707, + 22744, + 27578, + 21456, + 23822, + 27873, + 17403, + 25006, + 28749, + 13517, + 26218, + 29613, + 9816, + 27365, + 30767, + 6521, + 30772, + 33606, + 6593, + 34250, + 37331, + 7175, + 37156, + 39580, + 7500, + 7340, + 30443, + 58143, + 6856, + 31545, + 58390, + 5559, + 33313, + 59259, + 11109, + 31431, + 51177, + 15698, + 29346, + 43855, + 19254, + 27713, + 38497, + 21877, + 26745, + 34053, + 23807, + 26131, + 29808, + 25452, + 25703, + 25752, + 26481, + 25922, + 21577, + 27384, + 26641, + 17762, + 28400, + 27585, + 13935, + 29392, + 28458, + 10320, + 30200, + 29565, + 6738, + 33512, + 32771, + 7082, + 36581, + 35549, + 7419, + 40191, + 39103, + 8074, + 9193, + 26085, + 57715, + 9314, + 27261, + 58253, + 9862, + 29457, + 60076, + 15418, + 27739, + 51761, + 20096, + 26164, + 43978, + 23512, + 25055, + 38577, + 25998, + 24433, + 34152, + 27593, + 24104, + 29965, + 28839, + 23876, + 25794, + 29873, + 23842, + 21676, + 30845, + 24634, + 17890, + 31716, + 25629, + 14207, + 32560, + 26701, + 10742, + 33178, + 27928, + 7430, + 36652, + 31117, + 7742, + 39348, + 33456, + 7776, + 42956, + 37215, + 8242, + 11208, + 12349, + 53581, + 11921, + 21558, + 57880, + 14912, + 24539, + 61343, + 20469, + 23872, + 52729, + 25021, + 23081, + 44091, + 27816, + 22614, + 38852, + 29782, + 22284, + 34601, + 30912, + 22105, + 30779, + 32091, + 21703, + 26245, + 33045, + 21415, + 21912, + 34076, + 22079, + 17966, + 34821, + 22963, + 14312, + 35450, + 23912, + 10931, + 35975, + 25811, + 7991, + 39120, + 28679, + 8031, + 42160, + 31029, + 7930, + 44535, + 33615, + 8187, + 11203, + 12326, + 53621, + 11518, + 11927, + 53883, + 22285, + 19219, + 63408, + 26593, + 19870, + 54197, + 30211, + 20107, + 45106, + 32020, + 20276, + 39871, + 33081, + 20052, + 35606, + 34135, + 19754, + 31407, + 35298, + 19072, + 26721, + 36152, + 18761, + 22438, + 36983, + 18958, + 18356, + 37730, + 19657, + 14348, + 38300, + 20611, + 11039, + 38840, + 21923, + 7835, + 41674, + 25371, + 8093, + 44351, + 28445, + 7493, + 45747, + 30543, + 7679, + 11503, + 11881, + 53855, + 11568, + 11892, + 53898, + 29027, + 14851, + 65535, + 33503, + 15865, + 56318, + 35570, + 17040, + 48205, + 36220, + 17576, + 41923, + 36646, + 17634, + 37320, + 37721, + 17101, + 32439, + 38652, + 16215, + 27667, + 39318, + 15787, + 23349, + 39913, + 15416, + 19084, + 40594, + 15162, + 14610, + 41371, + 15753, + 10601, + 41976, + 16912, + 6785, + 44471, + 21333, + 7359, + 46110, + 24197, + 6624, + 48282, + 28130, + 6709, + 11433, + 12077, + 53434, + 30417, + 11360, + 60094, + 35047, + 11411, + 59356, + 40735, + 12532, + 59158, + 41552, + 13347, + 52326, + 41143, + 14252, + 45456, + 41012, + 14349, + 39923, + 41756, + 13722, + 34163, + 42320, + 13176, + 29126, + 42741, + 12718, + 24558, + 43147, + 12240, + 20107, + 44017, + 10934, + 15216, + 44897, + 10039, + 10128, + 47479, + 11565, + 7460, + 49719, + 16553, + 7730, + 50588, + 21046, + 8082, + 50870, + 25013, + 8032, + 32568, + 10641, + 58717, + 36013, + 10563, + 57899, + 39755, + 10712, + 57336, + 43782, + 10607, + 56626, + 47793, + 9451, + 56433, + 47342, + 9929, + 50048, + 46869, + 9953, + 43497, + 46812, + 9556, + 36941, + 46796, + 9253, + 31284, + 46683, + 8991, + 26129, + 46833, + 8177, + 21312, + 47528, + 6517, + 15873, + 48508, + 4793, + 10484, + 51054, + 9841, + 9938, + 51381, + 10835, + 9980, + 51530, + 16251, + 9160, + 51646, + 21014, + 8936, + 36232, + 10309, + 57466, + 40491, + 10455, + 56839, + 43294, + 10369, + 56342, + 46384, + 10004, + 56238, + 50476, + 8679, + 56306, + 52702, + 7789, + 53379, + 51862, + 7992, + 46962, + 51870, + 7430, + 40373, + 51508, + 7504, + 33853, + 51201, + 7725, + 28755, + 51442, + 7768, + 23821, + 51592, + 7381, + 19214, + 51987, + 7028, + 12239, + 52022, + 10042, + 10769, + 51990, + 10727, + 10539, + 51970, + 11107, + 10417, + 51957, + 11347, + 10341, + 41009, + 10354, + 56604, + 42960, + 10296, + 56256, + 44760, + 10046, + 55992, + 50102, + 9190, + 56607, + 54128, + 8008, + 56669, + 53501, + 8451, + 53102, + 53691, + 8209, + 48321, + 53714, + 7542, + 42140, + 53264, + 7685, + 35460, + 52695, + 8224, + 30491, + 52518, + 8457, + 25620, + 52587, + 8522, + 21425, + 52902, + 7770, + 14449, + 52360, + 10033, + 11113, + 52258, + 10559, + 10852, + 52193, + 10896, + 10691, + 52148, + 11130, + 10583, + 7905, + 44507, + 47398, + 8432, + 44332, + 45410, + 8478, + 44317, + 45025, + 8776, + 45034, + 44357, + 8803, + 45899, + 43751, + 9004, + 43809, + 40366, + 10043, + 43102, + 38539, + 10137, + 42870, + 37852, + 10432, + 42754, + 34937, + 10739, + 42456, + 32486, + 10624, + 42311, + 29997, + 9702, + 42286, + 26064, + 6291, + 42391, + 17749, + 7692, + 48341, + 10463, + 9093, + 50012, + 10290, + 9866, + 50879, + 10191, + 10278, + 50990, + 9997, + 7462, + 44568, + 50666, + 7553, + 44589, + 48774, + 8399, + 44218, + 45370, + 8458, + 44175, + 44830, + 8827, + 44843, + 43836, + 8629, + 45236, + 42813, + 9084, + 43372, + 39299, + 10054, + 42705, + 37789, + 10216, + 42539, + 35002, + 10714, + 42203, + 32366, + 10502, + 41748, + 28930, + 9529, + 41733, + 25293, + 5781, + 41419, + 16506, + 6896, + 42579, + 9485, + 8715, + 45374, + 8961, + 9704, + 47919, + 9039, + 10234, + 48618, + 8896, + 6078, + 47021, + 55627, + 7392, + 44412, + 50607, + 7464, + 44403, + 48852, + 7857, + 44130, + 45847, + 8415, + 43859, + 44400, + 8806, + 43943, + 42572, + 9196, + 42467, + 39875, + 9197, + 41410, + 36884, + 10647, + 40145, + 33897, + 11605, + 39025, + 30415, + 11100, + 38338, + 26319, + 8130, + 38520, + 20440, + 5808, + 39208, + 13927, + 6700, + 39373, + 7723, + 8746, + 41335, + 7154, + 9912, + 43802, + 7272, + 10706, + 45521, + 7684, + 6121, + 44646, + 56058, + 5675, + 46143, + 55518, + 7260, + 44114, + 50495, + 7287, + 44002, + 48704, + 7613, + 43566, + 45790, + 9546, + 41737, + 41839, + 10491, + 40470, + 38580, + 11638, + 39671, + 35817, + 12564, + 38451, + 32486, + 12912, + 37654, + 28880, + 12573, + 36861, + 24640, + 11189, + 36722, + 18464, + 9531, + 36237, + 11733, + 9687, + 35996, + 5614, + 10060, + 38072, + 4433, + 16846, + 40311, + 3904, + 25296, + 43570, + 4421, + 6546, + 43789, + 56264, + 6081, + 44156, + 55821, + 5034, + 44887, + 55121, + 5992, + 43625, + 50699, + 7453, + 41892, + 46848, + 10408, + 40408, + 42067, + 11963, + 38879, + 38236, + 13250, + 37546, + 34469, + 14123, + 36388, + 30940, + 14362, + 35505, + 27059, + 14250, + 34815, + 22229, + 14049, + 34564, + 16412, + 14739, + 34743, + 10938, + 15587, + 34547, + 5738, + 18997, + 37259, + 4986, + 23845, + 40029, + 5179, + 27637, + 42927, + 5203, + 6633, + 41213, + 56610, + 6857, + 41834, + 55912, + 5994, + 42693, + 55264, + 3885, + 42636, + 53195, + 8620, + 40215, + 47160, + 12163, + 38002, + 41659, + 13998, + 36491, + 37147, + 15416, + 35347, + 33410, + 16591, + 34121, + 29565, + 17061, + 33417, + 25167, + 17490, + 32843, + 20054, + 18498, + 32946, + 14869, + 20062, + 33541, + 11070, + 21790, + 33621, + 6538, + 25598, + 36325, + 5762, + 28782, + 38964, + 5994, + 31845, + 41370, + 7089, + 6853, + 38787, + 57460, + 6968, + 39344, + 56795, + 6281, + 40221, + 56118, + 6916, + 39767, + 53041, + 11177, + 37460, + 47081, + 14473, + 35446, + 41478, + 16914, + 34058, + 36716, + 18649, + 33012, + 32418, + 19988, + 32147, + 28413, + 21058, + 31573, + 24135, + 21980, + 31343, + 19438, + 23138, + 31862, + 15036, + 24522, + 32644, + 11493, + 26319, + 33340, + 7970, + 28662, + 35901, + 6466, + 31955, + 38816, + 6853, + 36224, + 41451, + 7529, + 7662, + 35612, + 58055, + 7534, + 36819, + 57913, + 7346, + 37728, + 57666, + 10016, + 36724, + 53309, + 14331, + 34524, + 46686, + 17656, + 32797, + 41388, + 20312, + 31484, + 36671, + 22216, + 30683, + 32214, + 23782, + 30079, + 28084, + 25020, + 29898, + 23744, + 26112, + 30152, + 19562, + 27183, + 30887, + 15565, + 28263, + 31710, + 11876, + 29576, + 32767, + 8246, + 31769, + 34794, + 6644, + 35075, + 38325, + 7295, + 38776, + 41564, + 7745, + 8276, + 31535, + 58833, + 8713, + 32522, + 58779, + 9759, + 34018, + 58741, + 13959, + 33518, + 53644, + 18288, + 31497, + 46688, + 21665, + 29909, + 41288, + 24155, + 28991, + 36610, + 26184, + 28499, + 32343, + 27897, + 28148, + 28200, + 28763, + 28179, + 23821, + 29718, + 28769, + 19826, + 30637, + 29622, + 16015, + 31594, + 30641, + 12375, + 32482, + 31611, + 8700, + 34380, + 33462, + 6957, + 37971, + 36960, + 7715, + 41052, + 39697, + 8232, + 9888, + 27434, + 58646, + 10603, + 28771, + 59498, + 13170, + 29759, + 59099, + 18715, + 29816, + 54011, + 22817, + 28338, + 46844, + 26064, + 27270, + 41250, + 28493, + 26793, + 36808, + 30045, + 26490, + 32560, + 31272, + 26327, + 28212, + 32299, + 26224, + 24011, + 33197, + 26687, + 20019, + 34036, + 27626, + 16223, + 34857, + 28704, + 12697, + 35541, + 29841, + 9225, + 37485, + 32030, + 7753, + 40590, + 34623, + 7947, + 44019, + 37909, + 8476, + 11221, + 12359, + 53593, + 13089, + 23140, + 58993, + 18280, + 25969, + 60729, + 23998, + 26076, + 54735, + 27840, + 25366, + 47159, + 30453, + 24988, + 41554, + 32402, + 24692, + 37234, + 33477, + 24475, + 33288, + 34625, + 24106, + 28586, + 35663, + 23908, + 24247, + 36597, + 24315, + 20138, + 37287, + 25160, + 16378, + 37947, + 26325, + 12916, + 38480, + 27452, + 9658, + 40232, + 29698, + 7977, + 43452, + 32200, + 8124, + 45755, + 35089, + 8135, + 11216, + 12336, + 53632, + 11544, + 11946, + 53906, + 25442, + 21434, + 61931, + 30120, + 22461, + 56034, + 33058, + 22566, + 48472, + 34707, + 22716, + 42801, + 35781, + 22543, + 38322, + 36896, + 22248, + 33864, + 37997, + 21571, + 29076, + 38846, + 21262, + 24633, + 39706, + 21470, + 20586, + 40436, + 22174, + 16519, + 40907, + 23167, + 13218, + 41335, + 24422, + 9780, + 43075, + 26595, + 7892, + 44981, + 29297, + 7552, + 47059, + 31767, + 7710, + 11516, + 11891, + 53866, + 11594, + 11911, + 53922, + 32136, + 16618, + 63859, + 37410, + 18698, + 58715, + 39027, + 19613, + 51389, + 38956, + 20202, + 44872, + 39349, + 20259, + 40113, + 40461, + 19630, + 34853, + 41466, + 18776, + 29784, + 42124, + 18327, + 25423, + 42771, + 18147, + 21124, + 43479, + 18107, + 16766, + 44060, + 19033, + 12638, + 44693, + 19712, + 8738, + 46019, + 21579, + 6624, + 48369, + 25485, + 6049, + 50052, + 28845, + 6882, + 11444, + 12087, + 53446, + 32302, + 12212, + 60889, + 35487, + 13391, + 60035, + 43956, + 15273, + 61810, + 44580, + 16074, + 55066, + 43896, + 17130, + 48113, + 43648, + 17175, + 42575, + 44573, + 16126, + 36496, + 45131, + 15243, + 31087, + 45686, + 14564, + 26473, + 46200, + 14032, + 22136, + 46943, + 13260, + 17378, + 47896, + 12604, + 11996, + 48628, + 13254, + 7352, + 50651, + 18194, + 8333, + 51130, + 22431, + 8462, + 51299, + 26167, + 8250, + 33287, + 11153, + 59272, + 36825, + 11168, + 58573, + 41314, + 11480, + 58114, + 45587, + 11780, + 57972, + 50687, + 12239, + 59091, + 50238, + 12351, + 52901, + 49759, + 12340, + 46297, + 49738, + 11820, + 39565, + 49732, + 11305, + 33590, + 49744, + 10725, + 28206, + 49978, + 9903, + 23263, + 50607, + 8426, + 17808, + 51529, + 7002, + 12378, + 51934, + 10771, + 10472, + 51921, + 11454, + 10279, + 51887, + 17835, + 9406, + 52079, + 22347, + 9156, + 37540, + 10731, + 57757, + 41537, + 10906, + 57283, + 44535, + 10925, + 56998, + 49204, + 10790, + 57417, + 52393, + 9972, + 57838, + 55897, + 7713, + 57173, + 55281, + 7666, + 50046, + 55082, + 7323, + 42897, + 53655, + 8117, + 35253, + 52978, + 8531, + 29896, + 52771, + 8675, + 24954, + 52866, + 8250, + 20027, + 53225, + 8108, + 13003, + 52450, + 10448, + 11075, + 52290, + 11014, + 10754, + 52201, + 11328, + 10582, + 52145, + 11529, + 10474, + 41417, + 10666, + 56971, + 44021, + 10657, + 56684, + 45960, + 10562, + 56730, + 50856, + 9852, + 57335, + 55734, + 8993, + 57810, + 54921, + 9171, + 54514, + 54878, + 9076, + 49362, + 54861, + 8531, + 43113, + 54201, + 8740, + 36257, + 53530, + 9004, + 31090, + 53278, + 9059, + 26129, + 53292, + 9067, + 21849, + 53548, + 8356, + 14975, + 52662, + 10320, + 11328, + 52491, + 10781, + 11018, + 52381, + 11077, + 10826, + 52307, + 11283, + 10696, + 7768, + 49483, + 54758, + 8527, + 44821, + 45912, + 8580, + 44876, + 45597, + 8926, + 45944, + 44997, + 8892, + 46810, + 44589, + 8965, + 46825, + 43054, + 10096, + 43770, + 39091, + 10202, + 43607, + 38325, + 10836, + 44368, + 36101, + 10961, + 42972, + 32568, + 10644, + 42769, + 30120, + 9803, + 42819, + 26370, + 6633, + 43433, + 18188, + 8603, + 51073, + 11134, + 9448, + 51194, + 10613, + 10004, + 51267, + 10299, + 10396, + 51318, + 10090, + 7736, + 49328, + 55074, + 7724, + 49304, + 54564, + 8162, + 44899, + 46230, + 8599, + 44945, + 45619, + 8987, + 46080, + 44815, + 8774, + 46699, + 44138, + 9274, + 46363, + 41820, + 10153, + 43819, + 38705, + 10816, + 44041, + 36294, + 10966, + 43153, + 32767, + 10554, + 42703, + 29364, + 9706, + 42650, + 25811, + 6323, + 43026, + 17179, + 8131, + 48076, + 10303, + 9497, + 49696, + 9867, + 10356, + 50862, + 9888, + 10760, + 50995, + 9723, + 6399, + 47561, + 56220, + 7002, + 48897, + 55485, + 7648, + 48993, + 54225, + 8156, + 44992, + 46840, + 8563, + 45061, + 45777, + 8993, + 46051, + 44486, + 9601, + 45191, + 42564, + 10525, + 44007, + 39344, + 10896, + 43699, + 36570, + 11060, + 42991, + 32990, + 10417, + 42200, + 28624, + 8341, + 41389, + 22372, + 6072, + 41466, + 14326, + 7943, + 43050, + 8790, + 9749, + 45558, + 8295, + 10505, + 47628, + 8399, + 18807, + 50219, + 7355, + 5981, + 45804, + 57107, + 6238, + 47165, + 56264, + 6739, + 48009, + 55287, + 7755, + 45150, + 50003, + 8187, + 45174, + 47544, + 8571, + 45743, + 45952, + 11221, + 43789, + 41773, + 12777, + 42054, + 38061, + 13317, + 41077, + 34724, + 13558, + 40257, + 31030, + 13406, + 39578, + 26527, + 12343, + 39122, + 20431, + 10744, + 39169, + 12920, + 11065, + 39029, + 6784, + 11608, + 41530, + 5093, + 17886, + 42626, + 4690, + 26125, + 44646, + 4882, + 6996, + 44733, + 57028, + 6654, + 44987, + 56770, + 5867, + 46280, + 56373, + 6289, + 46806, + 54671, + 7074, + 45749, + 50841, + 10710, + 43477, + 45321, + 13315, + 41320, + 40402, + 14454, + 39885, + 36770, + 15269, + 38790, + 33209, + 15863, + 38014, + 29247, + 15728, + 37329, + 24235, + 15570, + 37053, + 18247, + 16334, + 37099, + 12356, + 17361, + 37306, + 7226, + 20007, + 38822, + 4857, + 25951, + 42454, + 5065, + 28847, + 44074, + 5909, + 7194, + 42138, + 57356, + 7487, + 42993, + 56965, + 6914, + 44231, + 56823, + 5373, + 45267, + 56209, + 9879, + 42854, + 50308, + 13593, + 40522, + 44477, + 15534, + 38771, + 39437, + 17160, + 37637, + 35740, + 18728, + 36534, + 31919, + 19240, + 35935, + 27474, + 19796, + 35418, + 22353, + 20742, + 35431, + 16934, + 22145, + 35871, + 12819, + 23762, + 36209, + 8449, + 26460, + 37531, + 5776, + 29853, + 41085, + 6627, + 32706, + 43862, + 6145, + 7818, + 39601, + 57844, + 8086, + 40232, + 57553, + 7774, + 41689, + 57671, + 8704, + 42245, + 55961, + 12739, + 39962, + 50043, + 16471, + 37787, + 44203, + 18945, + 36292, + 39219, + 20798, + 35376, + 34771, + 22167, + 34580, + 30797, + 23290, + 34047, + 26507, + 24300, + 33834, + 21764, + 25414, + 34246, + 17279, + 26948, + 34948, + 13393, + 28270, + 35665, + 9795, + 29830, + 36953, + 6608, + 32671, + 39533, + 6934, + 37494, + 43493, + 6901, + 8785, + 36365, + 58109, + 9075, + 37492, + 58044, + 9822, + 38618, + 57994, + 12619, + 38867, + 55718, + 16584, + 36808, + 49748, + 19928, + 35039, + 44140, + 22484, + 33836, + 39270, + 24452, + 33066, + 34656, + 26079, + 32496, + 30485, + 27374, + 32256, + 26011, + 28428, + 32477, + 21764, + 29441, + 33125, + 17742, + 30426, + 33984, + 13868, + 31607, + 34834, + 10182, + 32726, + 35819, + 6787, + 35996, + 39312, + 7415, + 39139, + 42083, + 7838, + 9422, + 32090, + 58892, + 10240, + 33404, + 58846, + 11965, + 34988, + 58875, + 17093, + 35471, + 55684, + 20951, + 33623, + 49596, + 24144, + 32102, + 44075, + 26521, + 31394, + 39231, + 28567, + 30917, + 34828, + 30334, + 30590, + 30644, + 31119, + 30516, + 26133, + 32007, + 30959, + 22001, + 32883, + 31719, + 18173, + 33733, + 32624, + 14344, + 34614, + 33642, + 10693, + 35342, + 34895, + 7326, + 38981, + 38076, + 7934, + 42012, + 40868, + 8413, + 10674, + 28717, + 59512, + 11980, + 29552, + 59482, + 15553, + 31296, + 59449, + 22144, + 31716, + 55770, + 25561, + 30516, + 49628, + 28755, + 29592, + 43899, + 30927, + 29113, + 39375, + 32539, + 28869, + 35058, + 33802, + 28689, + 30651, + 34764, + 28544, + 26283, + 35680, + 28816, + 22092, + 36428, + 29612, + 18278, + 37158, + 30550, + 14499, + 37780, + 31662, + 11070, + 38561, + 33045, + 7866, + 41840, + 35937, + 8194, + 44711, + 38845, + 8586, + 11206, + 12362, + 53638, + 14880, + 24779, + 60590, + 21284, + 27398, + 60466, + 27652, + 28158, + 56363, + 30687, + 27614, + 50160, + 33140, + 27400, + 44485, + 35087, + 27162, + 40048, + 36093, + 26877, + 35814, + 37272, + 26439, + 30982, + 38305, + 26308, + 26540, + 39169, + 26590, + 22280, + 39767, + 27255, + 18385, + 40339, + 28076, + 14553, + 40894, + 29333, + 11409, + 41506, + 31039, + 8440, + 44486, + 33199, + 8168, + 47002, + 36116, + 8215, + 11227, + 12345, + 53644, + 11569, + 11965, + 53929, + 28411, + 23184, + 61714, + 33054, + 25271, + 57316, + 35783, + 25078, + 51860, + 37447, + 25199, + 46089, + 38618, + 25140, + 41339, + 39710, + 24730, + 36551, + 40759, + 24019, + 31489, + 41586, + 23714, + 26874, + 42381, + 23882, + 22779, + 42986, + 24462, + 18607, + 43524, + 25144, + 14505, + 43940, + 26063, + 11237, + 44256, + 27612, + 8099, + 46663, + 30103, + 7487, + 48907, + 33216, + 7156, + 11530, + 11900, + 53879, + 11619, + 11930, + 53944, + 34317, + 19063, + 62759, + 40497, + 21001, + 61045, + 41395, + 22280, + 54250, + 42007, + 22853, + 48172, + 42296, + 22899, + 43064, + 43347, + 22100, + 37529, + 44271, + 21267, + 32119, + 44914, + 20828, + 27595, + 45547, + 20661, + 23235, + 46283, + 20651, + 18808, + 46892, + 21303, + 14283, + 47331, + 21855, + 10436, + 47487, + 23853, + 7400, + 50008, + 27232, + 7122, + 50720, + 30256, + 7052, + 11457, + 12096, + 53458, + 33142, + 13810, + 60828, + 39200, + 13870, + 61699, + 45680, + 17073, + 60533, + 47740, + 18960, + 57774, + 47241, + 19565, + 51367, + 46704, + 19740, + 45408, + 47449, + 18679, + 39017, + 48023, + 17684, + 33225, + 48595, + 16949, + 28607, + 49238, + 16270, + 24100, + 49803, + 15425, + 19141, + 50717, + 14914, + 13655, + 51258, + 15638, + 9021, + 51547, + 20296, + 8928, + 51750, + 23840, + 8801, + 51805, + 27156, + 8233, + 34674, + 11622, + 59728, + 38184, + 11790, + 59129, + 42794, + 12239, + 58885, + 45695, + 13445, + 58333, + 53541, + 15085, + 61733, + 53011, + 15140, + 55566, + 52281, + 15100, + 48713, + 52328, + 14429, + 41813, + 52428, + 13651, + 35711, + 52539, + 12760, + 30202, + 52919, + 11777, + 25180, + 53609, + 10280, + 19755, + 54528, + 9194, + 14028, + 52660, + 10992, + 10992, + 52364, + 12079, + 10523, + 52377, + 19358, + 9647, + 52319, + 23155, + 9358, + 38818, + 11142, + 58040, + 42111, + 11349, + 57796, + 45528, + 11494, + 57664, + 50649, + 11661, + 58407, + 54323, + 11349, + 59400, + 55227, + 11866, + 55868, + 55435, + 11470, + 50404, + 55461, + 10790, + 43436, + 55151, + 10355, + 36763, + 54317, + 10170, + 31050, + 53940, + 9968, + 25889, + 53912, + 9556, + 20507, + 54233, + 9042, + 13806, + 52875, + 10854, + 11381, + 52589, + 11301, + 10968, + 52432, + 11551, + 10746, + 52552, + 18535, + 9902, + 41824, + 10980, + 57336, + 45026, + 11020, + 57124, + 47211, + 11075, + 57359, + 51953, + 10563, + 58221, + 54750, + 11061, + 56422, + 55274, + 10706, + 54931, + 55218, + 10520, + 49810, + 55250, + 10019, + 43729, + 54888, + 9851, + 36916, + 54165, + 9896, + 31628, + 53847, + 9856, + 26612, + 53887, + 9742, + 22275, + 54190, + 8944, + 15498, + 52963, + 10607, + 11545, + 52722, + 11003, + 11184, + 52570, + 11258, + 10961, + 52465, + 11436, + 10810, + 7327, + 50287, + 55625, + 8620, + 45312, + 46415, + 8681, + 45435, + 46169, + 9058, + 46878, + 45650, + 8984, + 47725, + 45429, + 9117, + 47819, + 43902, + 10029, + 46342, + 41412, + 10286, + 44338, + 38723, + 10834, + 44988, + 36337, + 11037, + 45397, + 34836, + 10659, + 43248, + 30256, + 9903, + 43353, + 26675, + 9734, + 51980, + 23379, + 8807, + 51687, + 11305, + 9614, + 51668, + 10748, + 10143, + 51655, + 10410, + 10514, + 51645, + 10183, + 7879, + 49858, + 55602, + 7308, + 50256, + 55560, + 8364, + 45488, + 46945, + 8669, + 45737, + 46513, + 7995, + 48147, + 46550, + 8924, + 48181, + 45466, + 9497, + 47705, + 42957, + 10145, + 45585, + 40081, + 10828, + 45140, + 36932, + 11044, + 45519, + 34895, + 10587, + 43611, + 29761, + 9847, + 43874, + 26557, + 7565, + 50953, + 20581, + 8936, + 51123, + 10921, + 9949, + 51261, + 10331, + 10529, + 51335, + 10023, + 10902, + 51381, + 9834, + 6955, + 48286, + 56849, + 7333, + 49901, + 56140, + 7272, + 50185, + 55448, + 7006, + 51895, + 54868, + 8848, + 46507, + 47005, + 8035, + 49039, + 47190, + 8776, + 49459, + 46445, + 9754, + 48148, + 42794, + 10511, + 47550, + 39484, + 10780, + 46757, + 35542, + 10229, + 45985, + 30831, + 8430, + 45435, + 23867, + 6304, + 45515, + 15557, + 8828, + 47787, + 9599, + 10345, + 49381, + 9200, + 11144, + 50832, + 9434, + 19405, + 50915, + 7561, + 6369, + 46811, + 57848, + 6747, + 48078, + 57021, + 7189, + 49268, + 56275, + 7189, + 50006, + 55211, + 6673, + 51193, + 54001, + 7278, + 50103, + 50364, + 10446, + 47867, + 45530, + 12690, + 45741, + 41126, + 13537, + 44680, + 37497, + 14237, + 43897, + 33773, + 14082, + 43142, + 28759, + 13201, + 42474, + 22242, + 11530, + 42904, + 13834, + 11738, + 42917, + 6810, + 16957, + 48085, + 6820, + 20816, + 49331, + 6585, + 29434, + 50461, + 6211, + 7454, + 45372, + 57759, + 7232, + 45816, + 57717, + 6679, + 47658, + 57597, + 7063, + 48706, + 56502, + 6684, + 49439, + 54753, + 10640, + 47065, + 48963, + 13768, + 44555, + 43668, + 15519, + 42925, + 39455, + 16815, + 41640, + 35557, + 17653, + 41024, + 31691, + 17710, + 40348, + 26418, + 17460, + 39964, + 20118, + 18136, + 40078, + 13625, + 19276, + 40497, + 8447, + 21090, + 41311, + 3746, + 26633, + 43887, + 4892, + 29868, + 45167, + 5849, + 7939, + 42782, + 57846, + 8228, + 44066, + 57862, + 7954, + 45674, + 58268, + 6955, + 47857, + 59134, + 10651, + 45873, + 53602, + 14311, + 43607, + 47789, + 17318, + 41339, + 41801, + 19107, + 40183, + 38130, + 20877, + 39173, + 34220, + 21512, + 38652, + 29884, + 22238, + 38213, + 24762, + 23228, + 38204, + 19267, + 24537, + 38554, + 14371, + 25745, + 39090, + 10301, + 27352, + 39462, + 5779, + 31358, + 42803, + 6656, + 36121, + 47329, + 5699, + 9168, + 39867, + 57656, + 9299, + 40911, + 57802, + 9643, + 42484, + 58053, + 10662, + 44680, + 58774, + 14715, + 42440, + 52830, + 18282, + 40305, + 47133, + 20945, + 38713, + 41691, + 22982, + 37850, + 37213, + 24406, + 37080, + 33180, + 25602, + 36570, + 28880, + 26673, + 36468, + 24009, + 27701, + 36743, + 19510, + 29134, + 37320, + 15068, + 30352, + 38021, + 11447, + 31111, + 38801, + 7213, + 35218, + 41376, + 7519, + 38413, + 44890, + 6652, + 9702, + 37089, + 58146, + 10374, + 37987, + 58021, + 11440, + 39402, + 58207, + 15020, + 41071, + 58257, + 18949, + 39061, + 52282, + 22187, + 37137, + 46785, + 24696, + 36196, + 41779, + 26779, + 35488, + 37152, + 28435, + 34942, + 32913, + 29774, + 34690, + 28386, + 30780, + 34886, + 24007, + 31695, + 35425, + 19889, + 32583, + 36161, + 15888, + 33581, + 36931, + 11986, + 34431, + 37968, + 8188, + 37314, + 40799, + 7559, + 40930, + 44026, + 7641, + 10395, + 32735, + 58924, + 11412, + 34227, + 58884, + 13670, + 36013, + 58620, + 19866, + 37563, + 57997, + 23416, + 35965, + 52235, + 26420, + 34416, + 46743, + 28945, + 33709, + 41908, + 30993, + 33353, + 37369, + 32788, + 33050, + 33104, + 33583, + 32941, + 28472, + 34421, + 33216, + 24199, + 35234, + 33804, + 20230, + 36003, + 34696, + 16451, + 36813, + 35726, + 12621, + 37508, + 36797, + 9031, + 40017, + 39109, + 8044, + 43452, + 42531, + 8460, + 11588, + 29405, + 59470, + 13036, + 29869, + 59090, + 18888, + 32241, + 59137, + 24905, + 33846, + 58182, + 28230, + 32906, + 52465, + 31311, + 31775, + 46527, + 33570, + 31532, + 42092, + 35123, + 31360, + 37696, + 36365, + 31127, + 33155, + 37341, + 30950, + 28651, + 38175, + 31132, + 24296, + 38968, + 31714, + 20314, + 39577, + 32574, + 16569, + 40145, + 33735, + 13022, + 40765, + 34843, + 9470, + 43043, + 37032, + 8234, + 45194, + 39664, + 8793, + 13088, + 24551, + 59715, + 18120, + 26559, + 60588, + 23798, + 28611, + 60060, + 30594, + 30434, + 58968, + 33292, + 30180, + 53214, + 35811, + 29853, + 47675, + 37762, + 29655, + 42898, + 38782, + 29502, + 38487, + 39936, + 29010, + 33490, + 40904, + 28748, + 28898, + 41645, + 28816, + 24513, + 42238, + 29351, + 20471, + 42771, + 30100, + 16605, + 43337, + 31312, + 13096, + 43764, + 32619, + 9744, + 45399, + 34085, + 8093, + 47608, + 36987, + 8301, + 11518, + 11927, + 53884, + 24621, + 22400, + 61649, + 30404, + 24800, + 61351, + 35953, + 27613, + 60029, + 38559, + 27669, + 54560, + 40413, + 27807, + 49223, + 41463, + 27791, + 44284, + 42463, + 27377, + 39251, + 43515, + 26622, + 33962, + 44268, + 26205, + 29278, + 44888, + 26215, + 24980, + 45431, + 26629, + 20737, + 45987, + 27239, + 16460, + 46299, + 28312, + 12803, + 46550, + 29590, + 9327, + 48023, + 31242, + 7245, + 50063, + 34157, + 7114, + 11543, + 11910, + 53891, + 30870, + 18974, + 63231, + 35644, + 20906, + 62243, + 41311, + 23547, + 60434, + 43834, + 25395, + 56317, + 45005, + 25502, + 51204, + 45325, + 25528, + 46015, + 46202, + 24737, + 40237, + 47101, + 23794, + 34583, + 47607, + 23395, + 29833, + 48103, + 23161, + 25352, + 48753, + 23016, + 20765, + 49295, + 23330, + 16036, + 49639, + 23886, + 11664, + 49805, + 25690, + 8387, + 50915, + 28498, + 7727, + 51256, + 31337, + 7477, + 11568, + 11892, + 53899, + 36180, + 13847, + 62333, + 41204, + 16173, + 61682, + 45146, + 19394, + 60649, + 50814, + 21666, + 60400, + 49617, + 22298, + 54106, + 49628, + 22393, + 48272, + 50174, + 21353, + 41821, + 50626, + 20356, + 35783, + 51048, + 19584, + 30847, + 51508, + 18856, + 26188, + 52158, + 18086, + 21054, + 52876, + 17589, + 15555, + 53450, + 18196, + 10587, + 52537, + 22029, + 9573, + 52223, + 25525, + 8892, + 52094, + 28172, + 8582, + 36138, + 12100, + 60018, + 39538, + 12573, + 59559, + 43438, + 13022, + 59198, + 49496, + 14895, + 60941, + 54068, + 16924, + 61239, + 54886, + 18366, + 57789, + 54152, + 18337, + 50867, + 54585, + 17376, + 44176, + 54708, + 16395, + 37774, + 54741, + 15428, + 32237, + 55106, + 14561, + 27322, + 56015, + 13075, + 21485, + 55560, + 11994, + 15015, + 53372, + 12174, + 11514, + 53088, + 16854, + 10434, + 52851, + 21682, + 9881, + 52714, + 24485, + 9539, + 39735, + 11679, + 58622, + 42684, + 11796, + 58306, + 46234, + 12079, + 58316, + 51025, + 12961, + 58713, + 54307, + 13079, + 57884, + 55427, + 13379, + 55893, + 55795, + 13054, + 50904, + 55880, + 12482, + 44106, + 55793, + 12036, + 37574, + 55232, + 11525, + 31887, + 54756, + 11136, + 26616, + 54631, + 10753, + 21260, + 54958, + 10191, + 15274, + 53297, + 11262, + 11686, + 52885, + 11589, + 11181, + 52660, + 11775, + 10913, + 52906, + 21206, + 9984, + 42231, + 11294, + 57700, + 45490, + 11402, + 57561, + 50971, + 11524, + 58319, + 53370, + 11260, + 59110, + 54933, + 12027, + 56359, + 55525, + 11824, + 55227, + 55458, + 11553, + 50092, + 55523, + 11096, + 43922, + 55395, + 10816, + 37419, + 54695, + 10670, + 32070, + 54332, + 10549, + 27019, + 54366, + 10412, + 22651, + 54665, + 9673, + 16031, + 53262, + 10895, + 11761, + 52953, + 11225, + 11349, + 52757, + 11438, + 11096, + 52623, + 11588, + 10923, + 7247, + 51187, + 56273, + 7407, + 52830, + 55878, + 8717, + 46041, + 46846, + 8040, + 48645, + 47064, + 9075, + 48646, + 46266, + 9269, + 48809, + 44743, + 10047, + 47460, + 42404, + 10322, + 46732, + 40702, + 10858, + 45561, + 36534, + 11052, + 45775, + 35101, + 11908, + 52020, + 34207, + 11708, + 52073, + 30400, + 10379, + 52309, + 23722, + 9014, + 52302, + 11476, + 9780, + 52145, + 10881, + 10282, + 52043, + 10520, + 10634, + 51972, + 10277, + 8023, + 50392, + 56131, + 7287, + 51265, + 56332, + 7407, + 52866, + 55876, + 6747, + 55255, + 55806, + 8080, + 49506, + 47669, + 9075, + 49679, + 46796, + 9721, + 49042, + 44085, + 10225, + 48210, + 42418, + 10862, + 46932, + 38154, + 11071, + 46205, + 35374, + 11893, + 52005, + 34018, + 11170, + 52153, + 27913, + 9801, + 52087, + 21930, + 9230, + 51982, + 11162, + 10166, + 51871, + 10504, + 10702, + 51807, + 10157, + 11047, + 51767, + 9944, + 7302, + 49177, + 57369, + 8053, + 50470, + 56337, + 7606, + 51299, + 56249, + 7407, + 52935, + 55872, + 6973, + 55096, + 55324, + 8224, + 51307, + 49266, + 7369, + 53908, + 50484, + 8803, + 52603, + 46430, + 10132, + 51864, + 42647, + 10613, + 50950, + 38173, + 10183, + 50273, + 32714, + 8525, + 49849, + 25197, + 6582, + 50190, + 16076, + 9744, + 51236, + 10445, + 10877, + 51379, + 9847, + 16511, + 51565, + 8442, + 19812, + 51689, + 8377, + 6879, + 47724, + 58452, + 7188, + 48812, + 57827, + 7633, + 50519, + 57242, + 7708, + 51548, + 56510, + 6959, + 53731, + 56176, + 6103, + 54796, + 54967, + 9748, + 52191, + 49565, + 12233, + 50216, + 44988, + 13638, + 49017, + 40774, + 14447, + 48160, + 36391, + 14355, + 47469, + 30895, + 13393, + 46878, + 23848, + 12642, + 47173, + 14931, + 13020, + 47281, + 7584, + 18447, + 49928, + 7304, + 22068, + 50573, + 6893, + 29814, + 51098, + 6606, + 8002, + 45924, + 58352, + 7421, + 47086, + 58885, + 7555, + 48928, + 58663, + 7907, + 50531, + 58209, + 7641, + 52828, + 58158, + 10857, + 50908, + 52756, + 14024, + 48608, + 47350, + 16540, + 46460, + 42492, + 18181, + 45374, + 38520, + 19681, + 44322, + 34445, + 19873, + 43838, + 28869, + 19811, + 43604, + 22250, + 20218, + 43802, + 14937, + 21598, + 44509, + 10123, + 24442, + 45307, + 5833, + 27606, + 45726, + 5143, + 32910, + 50275, + 5506, + 9248, + 43090, + 57722, + 9176, + 44809, + 58211, + 9379, + 46258, + 58652, + 9656, + 48574, + 59437, + 12015, + 49042, + 56896, + 15522, + 46903, + 51138, + 18985, + 44509, + 44749, + 21054, + 43176, + 40871, + 22961, + 42120, + 36702, + 23910, + 41512, + 32337, + 24675, + 41183, + 27213, + 25633, + 41201, + 21726, + 27066, + 41514, + 16435, + 28791, + 41950, + 12503, + 30305, + 42630, + 8179, + 32107, + 44838, + 5810, + 37207, + 48818, + 5576, + 9959, + 40105, + 57791, + 10260, + 41556, + 58013, + 10969, + 43193, + 58348, + 13112, + 44922, + 58512, + 17144, + 44667, + 54971, + 20354, + 42878, + 49719, + 23097, + 41376, + 44402, + 25232, + 40438, + 39803, + 26782, + 39695, + 35714, + 28057, + 39207, + 31328, + 29151, + 39103, + 26361, + 30164, + 39278, + 21697, + 31453, + 39778, + 17363, + 32678, + 40502, + 13386, + 34041, + 41111, + 9396, + 35992, + 43404, + 6781, + 39084, + 46557, + 6688, + 10469, + 37642, + 58187, + 11279, + 38677, + 58151, + 12581, + 40113, + 58382, + 17198, + 41716, + 58301, + 21518, + 40999, + 54159, + 24625, + 39253, + 49223, + 27061, + 38573, + 44465, + 29181, + 37990, + 39712, + 30913, + 37555, + 35446, + 32210, + 37300, + 30828, + 33228, + 37396, + 26311, + 34111, + 37780, + 21961, + 34929, + 38405, + 17979, + 35830, + 39156, + 13893, + 36675, + 39949, + 10050, + 38257, + 41938, + 7698, + 41777, + 44729, + 7774, + 11198, + 33509, + 58928, + 12330, + 35007, + 58907, + 16416, + 36722, + 58500, + 22218, + 38215, + 58019, + 26066, + 37893, + 54292, + 28821, + 36709, + 49233, + 31393, + 36106, + 44519, + 33484, + 35833, + 39999, + 35329, + 35595, + 35648, + 36157, + 35484, + 30902, + 36914, + 35623, + 26428, + 37684, + 36015, + 22161, + 38291, + 36810, + 18404, + 38905, + 37764, + 14372, + 39615, + 38506, + 10563, + 41107, + 40223, + 8249, + 44505, + 43761, + 8641, + 12349, + 29728, + 59487, + 14558, + 31267, + 59386, + 21703, + 32972, + 58810, + 26951, + 34813, + 58316, + 30974, + 34986, + 54652, + 33731, + 34235, + 49451, + 36062, + 34077, + 44865, + 37696, + 33867, + 40405, + 39006, + 33647, + 35779, + 39949, + 33423, + 31079, + 40617, + 33469, + 26602, + 41263, + 33891, + 22450, + 41851, + 34686, + 18510, + 42332, + 35553, + 14467, + 42820, + 36474, + 10881, + 44188, + 37964, + 8487, + 46382, + 40545, + 8383, + 13948, + 25479, + 59987, + 21400, + 27987, + 60113, + 27070, + 29626, + 59613, + 32659, + 31695, + 58991, + 36254, + 32520, + 55481, + 38557, + 32326, + 50526, + 40470, + 32210, + 45838, + 41509, + 32157, + 41417, + 42638, + 31530, + 36097, + 43564, + 31235, + 31265, + 44106, + 31208, + 26865, + 44644, + 31560, + 22671, + 45057, + 32136, + 18508, + 45528, + 32969, + 14388, + 45804, + 33860, + 10731, + 46922, + 35292, + 8135, + 48747, + 37363, + 7850, + 11531, + 11937, + 53895, + 27618, + 24260, + 61379, + 32335, + 26030, + 61225, + 37827, + 28947, + 60305, + 41681, + 30284, + 57245, + 42991, + 30251, + 52227, + 44353, + 30348, + 47211, + 45218, + 30012, + 41982, + 46214, + 29169, + 36446, + 46901, + 28806, + 31664, + 47272, + 28603, + 27207, + 47740, + 28865, + 22901, + 48171, + 29367, + 18451, + 48555, + 30101, + 14113, + 48748, + 30946, + 10026, + 49582, + 32127, + 6812, + 50556, + 34604, + 7013, + 11556, + 11919, + 53903, + 33446, + 21118, + 62723, + 37030, + 22678, + 61592, + 42417, + 25285, + 59774, + 46745, + 28038, + 59190, + 47703, + 28070, + 54193, + 48255, + 28091, + 49001, + 48980, + 27434, + 43115, + 49664, + 26451, + 37295, + 50004, + 25935, + 32258, + 50263, + 25676, + 27561, + 50643, + 25498, + 22790, + 51097, + 25686, + 17846, + 51552, + 26312, + 13028, + 51891, + 27149, + 8736, + 51877, + 29657, + 8137, + 51800, + 32420, + 7890, + 33710, + 13937, + 63012, + 37652, + 15427, + 62653, + 42921, + 18654, + 60796, + 46243, + 21143, + 60430, + 50421, + 23851, + 59833, + 51653, + 25686, + 56621, + 52260, + 24859, + 51246, + 52824, + 23945, + 45006, + 53180, + 23026, + 38953, + 53402, + 22238, + 33517, + 53665, + 21547, + 28405, + 53775, + 21244, + 23037, + 54246, + 21284, + 17426, + 54789, + 21558, + 12273, + 53351, + 23589, + 10199, + 52733, + 26827, + 9306, + 52505, + 29303, + 8898, + 35836, + 13288, + 59844, + 41617, + 13194, + 59691, + 45104, + 13965, + 60434, + 50791, + 16506, + 61238, + 53339, + 19297, + 60297, + 55296, + 20290, + 57167, + 56517, + 21236, + 53541, + 57044, + 20237, + 46932, + 57156, + 19155, + 40242, + 56975, + 18186, + 34473, + 57289, + 17289, + 29466, + 56753, + 14908, + 22533, + 56115, + 13517, + 15861, + 54225, + 13334, + 12047, + 53611, + 18822, + 10830, + 53361, + 23010, + 10174, + 52855, + 26192, + 9514, + 40571, + 12064, + 59040, + 43256, + 12244, + 58814, + 45460, + 13113, + 57894, + 53155, + 13579, + 60327, + 55658, + 13564, + 58397, + 55564, + 14406, + 55928, + 56066, + 14269, + 51256, + 56133, + 13521, + 44469, + 56117, + 13111, + 38002, + 55912, + 12588, + 32507, + 55387, + 12065, + 27180, + 55207, + 11727, + 21862, + 55374, + 11239, + 15918, + 52946, + 12931, + 12761, + 53180, + 11879, + 11396, + 52885, + 12001, + 11079, + 53301, + 22661, + 10161, + 42638, + 11610, + 58061, + 45956, + 11788, + 57994, + 51910, + 11841, + 59232, + 55218, + 12115, + 59008, + 55070, + 12747, + 56310, + 55644, + 12670, + 55384, + 55637, + 12326, + 50269, + 55729, + 11914, + 44184, + 55648, + 11638, + 37722, + 55144, + 11347, + 32439, + 54750, + 11153, + 27367, + 54785, + 11005, + 22972, + 55054, + 10360, + 16515, + 53558, + 11183, + 11976, + 53182, + 11447, + 11516, + 52944, + 11619, + 11231, + 52780, + 11742, + 11037, + 7711, + 51928, + 56549, + 7620, + 53372, + 56399, + 6955, + 56080, + 56629, + 8097, + 49677, + 47848, + 9168, + 49572, + 47106, + 9422, + 49793, + 45579, + 10073, + 48308, + 43117, + 10360, + 47985, + 41695, + 11506, + 52613, + 40907, + 11963, + 52113, + 37973, + 11927, + 52039, + 34216, + 11781, + 52134, + 30275, + 10784, + 52513, + 23938, + 9670, + 52729, + 11794, + 9950, + 52619, + 11016, + 10423, + 52431, + 10630, + 10755, + 52300, + 10371, + 8169, + 50925, + 56659, + 7789, + 52123, + 56745, + 7689, + 53577, + 56559, + 7205, + 56278, + 56551, + 6719, + 56861, + 55566, + 7845, + 52170, + 49588, + 8769, + 52883, + 47899, + 10003, + 50141, + 43867, + 11368, + 52923, + 42117, + 11901, + 52198, + 38409, + 11921, + 52037, + 33880, + 11492, + 52351, + 27964, + 10731, + 52606, + 20761, + 9765, + 52740, + 11509, + 10388, + 52482, + 10677, + 10877, + 52281, + 10292, + 11191, + 52153, + 10054, + 7622, + 50088, + 57905, + 8249, + 51168, + 57030, + 7928, + 52444, + 57083, + 7272, + 54808, + 57294, + 7240, + 56719, + 56785, + 7352, + 57317, + 55108, + 8851, + 56348, + 51963, + 10175, + 55125, + 48225, + 11603, + 53654, + 43621, + 11957, + 52406, + 38926, + 11923, + 52049, + 33353, + 11084, + 52554, + 25895, + 9824, + 52942, + 17147, + 7864, + 53083, + 8520, + 11195, + 52231, + 10090, + 17125, + 52436, + 8388, + 20223, + 52353, + 8677, + 7483, + 48502, + 58864, + 7826, + 49807, + 58500, + 8553, + 51455, + 57519, + 8377, + 52930, + 57609, + 7675, + 55874, + 58002, + 6606, + 58982, + 58850, + 9787, + 56475, + 53652, + 13096, + 54328, + 48605, + 14876, + 53052, + 44020, + 15829, + 52174, + 38824, + 15916, + 51699, + 33018, + 15295, + 51495, + 25061, + 14116, + 51719, + 15930, + 14992, + 51786, + 8650, + 19841, + 51839, + 8465, + 24499, + 52052, + 7774, + 30774, + 51995, + 7071, + 8726, + 46207, + 58544, + 8293, + 47838, + 59257, + 8799, + 49444, + 58785, + 9311, + 51294, + 58494, + 10267, + 53480, + 58209, + 11807, + 54957, + 56582, + 15620, + 52459, + 51140, + 18175, + 50660, + 46399, + 20578, + 49094, + 41792, + 21645, + 48384, + 37131, + 22197, + 48010, + 31315, + 22456, + 47696, + 24579, + 22988, + 48027, + 16997, + 23990, + 48571, + 11241, + 26802, + 49116, + 6762, + 31526, + 50924, + 6116, + 35135, + 51417, + 5098, + 9852, + 43739, + 57987, + 9947, + 45520, + 58529, + 10471, + 46698, + 58940, + 11227, + 49175, + 59569, + 13621, + 51550, + 59704, + 17996, + 49964, + 53813, + 21042, + 48220, + 48508, + 23640, + 46408, + 43923, + 25372, + 45399, + 39571, + 26412, + 44794, + 35112, + 27346, + 44513, + 29697, + 28243, + 44499, + 24081, + 29631, + 44749, + 18576, + 31018, + 45496, + 13718, + 32334, + 46283, + 9251, + 33748, + 47626, + 5133, + 37734, + 49757, + 5473, + 10609, + 40564, + 57945, + 11033, + 42178, + 58200, + 12228, + 43714, + 58272, + 14406, + 45897, + 59228, + 19938, + 46399, + 56007, + 22720, + 45743, + 52185, + 25432, + 44227, + 47293, + 27679, + 43275, + 42607, + 29425, + 42516, + 38359, + 30725, + 42077, + 33846, + 31797, + 41925, + 28850, + 32830, + 42124, + 24085, + 33862, + 42501, + 19371, + 34785, + 43342, + 14692, + 36009, + 44089, + 10744, + 37190, + 45102, + 6635, + 40322, + 48367, + 6534, + 11231, + 37968, + 58111, + 12001, + 39347, + 58259, + 13419, + 40668, + 58533, + 19455, + 42493, + 58510, + 24095, + 42835, + 55781, + 26801, + 41665, + 51374, + 29472, + 41164, + 47118, + 31689, + 40643, + 42423, + 33543, + 40257, + 38046, + 34840, + 40027, + 33353, + 35802, + 39991, + 28738, + 36586, + 40282, + 24182, + 37234, + 40847, + 19983, + 37956, + 41581, + 15772, + 38769, + 42364, + 11737, + 39333, + 43408, + 7945, + 43131, + 46531, + 7865, + 11877, + 34260, + 58926, + 13182, + 35941, + 58595, + 18814, + 37320, + 58390, + 23986, + 39082, + 58295, + 28506, + 39801, + 55944, + 31466, + 39314, + 51759, + 33933, + 38641, + 47162, + 36065, + 38459, + 42691, + 37915, + 38183, + 38236, + 38759, + 38151, + 33469, + 39485, + 38097, + 28841, + 40029, + 38316, + 24383, + 40510, + 39033, + 20356, + 41060, + 39887, + 16301, + 41638, + 40729, + 12444, + 42212, + 41483, + 8569, + 45271, + 44848, + 8430, + 12988, + 29989, + 59073, + 18647, + 32456, + 59091, + 23431, + 33893, + 58900, + 28845, + 35718, + 58542, + 33463, + 37253, + 57228, + 36336, + 37000, + 52318, + 38740, + 36658, + 47732, + 40347, + 36538, + 43187, + 41676, + 36315, + 38489, + 42535, + 36034, + 33600, + 43153, + 35907, + 28939, + 43613, + 36095, + 24619, + 44049, + 36652, + 20438, + 44484, + 37452, + 16291, + 44855, + 38246, + 12480, + 45338, + 39044, + 8721, + 47502, + 41746, + 8515, + 17985, + 27152, + 60457, + 23098, + 29196, + 59996, + 28803, + 30578, + 59638, + 34613, + 32957, + 59331, + 38962, + 34907, + 58293, + 41334, + 34914, + 53225, + 43180, + 34849, + 48783, + 44360, + 34743, + 44172, + 45367, + 34191, + 38801, + 46049, + 33749, + 33800, + 46587, + 33649, + 29143, + 46859, + 33775, + 24853, + 47147, + 34135, + 20442, + 47476, + 34748, + 16038, + 47825, + 35424, + 12131, + 48099, + 35979, + 8025, + 49909, + 38447, + 7831, + 11543, + 11946, + 53906, + 29341, + 24992, + 61274, + 34954, + 27557, + 60592, + 38801, + 29911, + 60276, + 44448, + 32826, + 60143, + 45899, + 33016, + 54929, + 47147, + 32946, + 50113, + 47939, + 32610, + 44843, + 48915, + 31835, + 39092, + 49236, + 31324, + 34104, + 49489, + 31060, + 29520, + 49736, + 31134, + 25066, + 49987, + 31402, + 20379, + 50219, + 31938, + 15608, + 50557, + 32436, + 11126, + 50704, + 32969, + 6865, + 51260, + 35481, + 7070, + 11569, + 11929, + 53915, + 34329, + 22165, + 62433, + 39063, + 24572, + 60468, + 43330, + 26217, + 59769, + 47694, + 28900, + 60059, + 50339, + 30923, + 57279, + 50852, + 30564, + 51909, + 51370, + 30100, + 46131, + 51916, + 29143, + 40413, + 52183, + 28498, + 35051, + 52399, + 28000, + 30033, + 52585, + 27762, + 25011, + 52898, + 27840, + 19925, + 53216, + 28312, + 14762, + 53440, + 29162, + 9931, + 52586, + 31220, + 8890, + 52355, + 33560, + 8365, + 36001, + 15454, + 63048, + 39487, + 17709, + 61775, + 42777, + 19932, + 60527, + 47002, + 23367, + 59744, + 51206, + 25272, + 59780, + 54069, + 28411, + 59556, + 54473, + 27914, + 54083, + 55062, + 27036, + 47782, + 55364, + 26015, + 41497, + 55473, + 25163, + 35847, + 55433, + 24545, + 30547, + 55544, + 23933, + 25157, + 55884, + 24005, + 19332, + 56460, + 23919, + 14013, + 54283, + 25219, + 10841, + 53258, + 28143, + 9742, + 52932, + 30447, + 9238, + 38419, + 13043, + 60770, + 42549, + 13942, + 60671, + 46669, + 15226, + 61445, + 50484, + 19190, + 60611, + 53330, + 21419, + 59482, + 54781, + 22394, + 56667, + 56247, + 22997, + 54110, + 57355, + 21602, + 47464, + 57042, + 20782, + 40872, + 57591, + 19407, + 35051, + 57926, + 18196, + 29748, + 57426, + 16694, + 23503, + 56646, + 15036, + 16692, + 55461, + 16663, + 12672, + 54269, + 20450, + 11219, + 53674, + 23950, + 10444, + 53176, + 27375, + 9738, + 41573, + 12461, + 59042, + 42913, + 13315, + 57321, + 46422, + 13750, + 58622, + 53960, + 14437, + 61008, + 55795, + 14273, + 57861, + 55798, + 17089, + 56028, + 56699, + 17462, + 52957, + 56494, + 15053, + 45142, + 56337, + 13841, + 38260, + 56439, + 13444, + 32980, + 55889, + 12821, + 27624, + 55679, + 12539, + 22354, + 55699, + 12073, + 16443, + 54130, + 12081, + 12292, + 53471, + 12172, + 11613, + 53680, + 19223, + 10839, + 53602, + 23216, + 10360, + 43391, + 12060, + 58338, + 46424, + 12178, + 58422, + 51233, + 13037, + 58156, + 54646, + 13015, + 57156, + 55175, + 13305, + 56272, + 55688, + 13309, + 55459, + 55776, + 12925, + 50377, + 55889, + 12555, + 44364, + 55849, + 12284, + 37933, + 55530, + 11944, + 32748, + 55114, + 11687, + 27667, + 55155, + 11535, + 23249, + 55355, + 10995, + 16953, + 53855, + 11471, + 12190, + 53410, + 11670, + 11681, + 53130, + 11802, + 11364, + 52936, + 11898, + 11151, + 7894, + 52771, + 57032, + 7838, + 53912, + 56916, + 7324, + 56818, + 57112, + 7175, + 57175, + 56589, + 9261, + 50503, + 47945, + 8193, + 52993, + 48665, + 9147, + 50583, + 45120, + 11014, + 53806, + 44555, + 11691, + 52729, + 40909, + 12039, + 52151, + 37888, + 11939, + 52052, + 34220, + 11836, + 52182, + 30139, + 11062, + 52653, + 24087, + 10244, + 53014, + 12060, + 10332, + 52959, + 11288, + 10565, + 52818, + 10741, + 10876, + 52627, + 10464, + 8316, + 51461, + 57194, + 8027, + 53079, + 57352, + 7392, + 55232, + 57709, + 7392, + 57296, + 57464, + 7479, + 57698, + 56264, + 8944, + 56730, + 53503, + 9356, + 56186, + 50816, + 10879, + 54581, + 46428, + 11693, + 53147, + 42195, + 12011, + 52271, + 38348, + 11934, + 52052, + 33732, + 11668, + 52461, + 27941, + 11370, + 52946, + 21046, + 10550, + 53133, + 11938, + 10662, + 53051, + 10900, + 11052, + 52754, + 10428, + 11336, + 52539, + 10164, + 8097, + 50808, + 58179, + 8610, + 51659, + 57479, + 8443, + 53349, + 57628, + 7823, + 55958, + 58052, + 7810, + 57950, + 57865, + 8778, + 57746, + 55374, + 10163, + 56924, + 52345, + 11172, + 55783, + 48723, + 12197, + 53990, + 43830, + 12098, + 52444, + 38837, + 11955, + 52037, + 33186, + 11611, + 52828, + 26015, + 11191, + 53777, + 16808, + 11275, + 53404, + 11670, + 11516, + 53083, + 10335, + 18896, + 53339, + 8715, + 21316, + 53085, + 8229, + 8255, + 48978, + 58852, + 8580, + 50366, + 58556, + 9214, + 51843, + 57785, + 9349, + 53638, + 57922, + 9014, + 56728, + 58440, + 10316, + 58184, + 57633, + 11940, + 57764, + 54469, + 14321, + 58213, + 52045, + 16318, + 57122, + 47270, + 17650, + 56144, + 41632, + 17711, + 55899, + 35371, + 17284, + 55705, + 26939, + 16152, + 56082, + 17365, + 17441, + 55114, + 10602, + 21273, + 53671, + 8514, + 28821, + 53515, + 7808, + 31409, + 52847, + 7575, + 9356, + 46452, + 58711, + 9131, + 48407, + 59377, + 9584, + 50031, + 59070, + 10297, + 51946, + 58646, + 12182, + 54032, + 58189, + 15080, + 57321, + 58660, + 18234, + 55886, + 54319, + 20708, + 54052, + 49660, + 22495, + 52767, + 44800, + 23787, + 51968, + 39858, + 24186, + 51723, + 33754, + 24547, + 51620, + 27017, + 25011, + 51907, + 19582, + 27057, + 52448, + 12707, + 29430, + 52847, + 7643, + 31533, + 52218, + 7076, + 36970, + 52166, + 5970, + 10371, + 44381, + 58242, + 10584, + 46207, + 58824, + 11033, + 47483, + 59405, + 12248, + 49700, + 59607, + 16218, + 52206, + 59097, + 21244, + 52546, + 55737, + 23441, + 51535, + 51834, + 26004, + 49866, + 47215, + 27761, + 48989, + 42809, + 28995, + 48230, + 38027, + 29864, + 48074, + 32602, + 30793, + 48093, + 26734, + 31953, + 48306, + 20672, + 33309, + 48916, + 14922, + 34613, + 49671, + 10128, + 35169, + 49793, + 5541, + 38556, + 51047, + 4747, + 11149, + 41145, + 58086, + 11670, + 42781, + 58371, + 12756, + 44218, + 58588, + 16455, + 46780, + 59523, + 22345, + 48960, + 58268, + 25355, + 48367, + 54282, + 27992, + 47261, + 50117, + 30263, + 46354, + 45663, + 32129, + 45736, + 41404, + 33405, + 45250, + 36861, + 34460, + 44969, + 31630, + 35496, + 45085, + 26561, + 36339, + 45429, + 21509, + 37118, + 46163, + 16590, + 37784, + 46967, + 11921, + 38850, + 47521, + 7287, + 41277, + 49694, + 6528, + 11763, + 38614, + 58196, + 12596, + 39993, + 58369, + 15549, + 41039, + 58497, + 21596, + 43303, + 58629, + 26420, + 44897, + 57838, + 29598, + 44802, + 53958, + 32050, + 43849, + 49767, + 34311, + 43427, + 45310, + 36243, + 43098, + 40869, + 37533, + 42828, + 36091, + 38443, + 42733, + 31274, + 39126, + 42893, + 26511, + 39683, + 43338, + 21974, + 40261, + 44066, + 17658, + 40943, + 44838, + 13438, + 41549, + 45697, + 9453, + 44087, + 47882, + 7869, + 12452, + 34988, + 58921, + 15479, + 36705, + 58692, + 21664, + 37777, + 57960, + 25539, + 39877, + 58494, + 30942, + 42109, + 58141, + 34052, + 41752, + 54039, + 36593, + 41279, + 49791, + 38816, + 41130, + 45443, + 40573, + 40843, + 40900, + 41411, + 40837, + 36067, + 42022, + 40649, + 31322, + 42454, + 40794, + 26629, + 42878, + 41277, + 22319, + 43260, + 42087, + 18240, + 43784, + 42874, + 14139, + 44393, + 43626, + 10208, + 46597, + 46165, + 8658, + 14195, + 30619, + 58285, + 21459, + 32864, + 58804, + 25425, + 34405, + 58148, + 30440, + 36659, + 58732, + 36110, + 39259, + 58784, + 39168, + 39406, + 54589, + 41313, + 39126, + 50276, + 43079, + 39088, + 45895, + 44408, + 38935, + 41166, + 45058, + 38607, + 36138, + 45555, + 38396, + 31366, + 45882, + 38430, + 26848, + 46150, + 38833, + 22444, + 46513, + 39417, + 18181, + 46878, + 40081, + 14066, + 47296, + 40872, + 10221, + 48942, + 43319, + 8584, + 21507, + 28674, + 60021, + 25568, + 29873, + 59477, + 30820, + 31284, + 59692, + 35372, + 33784, + 59165, + 40672, + 36585, + 59091, + 44265, + 37485, + 55642, + 45899, + 37422, + 51465, + 47127, + 37396, + 46968, + 47886, + 36856, + 41504, + 48481, + 36380, + 36287, + 48755, + 36100, + 31558, + 49013, + 35994, + 27057, + 49148, + 36249, + 22579, + 49339, + 36574, + 18061, + 49576, + 37097, + 13581, + 49869, + 37751, + 9417, + 50643, + 39402, + 7866, + 27224, + 24924, + 61251, + 31594, + 26474, + 61111, + 35572, + 28696, + 60886, + 40084, + 30843, + 60301, + 45553, + 34076, + 60128, + 48766, + 35760, + 57150, + 49700, + 35553, + 52918, + 50400, + 35231, + 47683, + 51000, + 34515, + 41881, + 51315, + 33962, + 36610, + 51419, + 33636, + 31843, + 51539, + 33456, + 27222, + 51752, + 33477, + 22370, + 51984, + 33725, + 17440, + 52186, + 34189, + 12661, + 52402, + 34699, + 8390, + 52237, + 36566, + 7660, + 30387, + 22240, + 62231, + 35506, + 24051, + 61742, + 40166, + 25302, + 60341, + 44349, + 27544, + 59824, + 48333, + 30525, + 59538, + 53054, + 33566, + 60116, + 53267, + 33446, + 54568, + 53815, + 32792, + 48982, + 54188, + 32014, + 43010, + 54266, + 31326, + 37344, + 54315, + 30799, + 32288, + 54291, + 30391, + 27239, + 54465, + 30320, + 21963, + 54670, + 30502, + 16494, + 55015, + 31052, + 11488, + 53403, + 32505, + 9637, + 52934, + 34621, + 8850, + 36396, + 17508, + 62661, + 40715, + 19196, + 61314, + 43963, + 21531, + 60198, + 47699, + 24902, + 59607, + 51674, + 26659, + 59633, + 54952, + 28907, + 59134, + 55622, + 29255, + 55260, + 56549, + 28904, + 49582, + 56846, + 27831, + 43357, + 56792, + 27008, + 37348, + 56618, + 26294, + 32029, + 55990, + 25756, + 26298, + 57009, + 24642, + 20299, + 57095, + 25490, + 14823, + 54653, + 27120, + 11425, + 53690, + 29351, + 10253, + 53254, + 31813, + 9563, + 39077, + 13536, + 61282, + 44167, + 14774, + 61394, + 48314, + 17907, + 60962, + 51223, + 20063, + 60527, + 53963, + 23134, + 59065, + 55211, + 24053, + 56802, + 56019, + 24279, + 54515, + 56762, + 23539, + 48099, + 56985, + 22187, + 41315, + 56907, + 21330, + 35712, + 56996, + 20476, + 30464, + 57534, + 18946, + 24562, + 57194, + 17768, + 18032, + 56372, + 18679, + 13355, + 54889, + 22844, + 11590, + 54169, + 25349, + 10720, + 53507, + 28525, + 9978, + 42655, + 12825, + 59217, + 45557, + 13319, + 59702, + 49923, + 14635, + 60853, + 54228, + 15872, + 61054, + 55323, + 17570, + 56891, + 55933, + 18588, + 56064, + 57002, + 19082, + 53510, + 56994, + 17468, + 46148, + 56793, + 15393, + 38921, + 56678, + 14133, + 33245, + 56298, + 13446, + 27981, + 56070, + 13229, + 22762, + 55961, + 12754, + 16884, + 54537, + 12499, + 12598, + 53760, + 12465, + 11828, + 54115, + 21918, + 10984, + 53786, + 24201, + 10512, + 44412, + 12387, + 58683, + 46894, + 12569, + 58850, + 53058, + 13275, + 60046, + 54874, + 13699, + 56821, + 55258, + 13750, + 56241, + 55722, + 13803, + 55517, + 55888, + 13403, + 50436, + 56017, + 13071, + 44484, + 56009, + 12805, + 38084, + 55866, + 12473, + 33007, + 55433, + 12161, + 27924, + 55272, + 11911, + 22950, + 55584, + 11564, + 17339, + 54149, + 11761, + 12404, + 53638, + 11893, + 11845, + 53313, + 11986, + 11501, + 53091, + 12053, + 11267, + 8090, + 53614, + 57502, + 7454, + 55471, + 57926, + 7498, + 57521, + 57739, + 7506, + 57964, + 57283, + 8932, + 56897, + 54241, + 9444, + 56542, + 51780, + 9271, + 53214, + 47642, + 11313, + 53977, + 44648, + 11826, + 52805, + 40883, + 12096, + 52166, + 37791, + 11944, + 52058, + 34106, + 11881, + 52222, + 29995, + 11264, + 52754, + 24195, + 10679, + 53227, + 12261, + 10742, + 53170, + 11575, + 10805, + 53117, + 10952, + 10997, + 52954, + 10558, + 8639, + 51765, + 57460, + 8479, + 53756, + 57633, + 7877, + 56011, + 58070, + 7928, + 57833, + 57907, + 8498, + 57907, + 56368, + 9664, + 57034, + 53716, + 9938, + 56566, + 51098, + 11313, + 54842, + 46589, + 11902, + 53275, + 42210, + 12081, + 52300, + 38260, + 11940, + 52062, + 33579, + 11783, + 52537, + 27884, + 11790, + 53170, + 21210, + 11083, + 53396, + 12228, + 11153, + 53311, + 11339, + 11229, + 53226, + 10563, + 11482, + 52925, + 10276, + 8648, + 51343, + 58205, + 9069, + 51919, + 57671, + 9356, + 53857, + 57644, + 8580, + 56640, + 58308, + 8717, + 58383, + 58206, + 9731, + 58000, + 55507, + 10955, + 57245, + 52548, + 11743, + 56143, + 49004, + 12474, + 54133, + 43902, + 12309, + 52309, + 38538, + 11944, + 51653, + 32859, + 11986, + 52724, + 26003, + 11938, + 54161, + 16964, + 11899, + 53727, + 12171, + 11954, + 53577, + 10904, + 20122, + 54301, + 8871, + 22463, + 53881, + 8368, + 8909, + 49434, + 58823, + 9204, + 50910, + 58592, + 9761, + 52155, + 57994, + 10580, + 54116, + 57860, + 9995, + 57443, + 58741, + 11661, + 58211, + 57557, + 12674, + 57954, + 54575, + 15196, + 58445, + 52250, + 17477, + 57569, + 47661, + 19130, + 56342, + 41967, + 18847, + 56456, + 35654, + 18557, + 56264, + 27305, + 17679, + 56659, + 17888, + 19238, + 55931, + 11916, + 22889, + 55612, + 9109, + 29955, + 54125, + 8804, + 32047, + 53695, + 8080, + 9913, + 46667, + 58854, + 9862, + 48875, + 59354, + 10284, + 50575, + 59101, + 11064, + 52553, + 58540, + 13168, + 54617, + 58291, + 16496, + 57357, + 58196, + 19917, + 58202, + 56505, + 23159, + 57546, + 52978, + 25791, + 55236, + 47552, + 26810, + 54746, + 42375, + 27479, + 54478, + 36574, + 28160, + 54452, + 30026, + 28985, + 54575, + 23144, + 29761, + 56045, + 14220, + 31009, + 55517, + 9638, + 33602, + 53782, + 8071, + 38165, + 53054, + 6746, + 10819, + 45013, + 58489, + 11221, + 46509, + 58977, + 11724, + 48207, + 59738, + 12944, + 50152, + 59607, + 19371, + 53077, + 59040, + 24018, + 55456, + 58355, + 26431, + 54481, + 54320, + 28508, + 53113, + 50219, + 30271, + 52136, + 45808, + 31653, + 51457, + 40812, + 32617, + 51147, + 35538, + 33619, + 51060, + 29672, + 34485, + 51198, + 23420, + 35148, + 51777, + 17154, + 36635, + 52105, + 11208, + 37616, + 52414, + 6059, + 39890, + 52279, + 5863, + 11615, + 41719, + 58219, + 12338, + 43290, + 58367, + 13515, + 44741, + 58381, + 19681, + 47306, + 59255, + 23952, + 50006, + 58653, + 28213, + 51433, + 56858, + 30584, + 50573, + 52982, + 32843, + 49605, + 48725, + 34762, + 48924, + 44497, + 36067, + 48426, + 39706, + 37149, + 48113, + 34538, + 38144, + 48021, + 29216, + 38842, + 48215, + 23929, + 39406, + 48788, + 18711, + 40181, + 49442, + 13516, + 40910, + 50050, + 8502, + 42246, + 51080, + 6546, + 12216, + 39250, + 58274, + 13091, + 40571, + 58492, + 17793, + 41781, + 58350, + 23361, + 44078, + 58536, + 27888, + 45952, + 58089, + 32300, + 47342, + 55923, + 34815, + 47120, + 52261, + 36985, + 46382, + 48042, + 39010, + 46015, + 43727, + 40248, + 45752, + 38932, + 41136, + 45577, + 33973, + 41762, + 45677, + 29038, + 42136, + 45949, + 24208, + 42627, + 46562, + 19649, + 43204, + 47268, + 15025, + 43760, + 48072, + 10685, + 45363, + 49540, + 7751, + 13062, + 35914, + 58588, + 17631, + 37266, + 58570, + 23555, + 38675, + 58198, + 27420, + 40784, + 58643, + 32379, + 42786, + 58395, + 36905, + 44401, + 56452, + 39274, + 44294, + 52459, + 41611, + 43849, + 48270, + 43336, + 43608, + 43669, + 44104, + 43568, + 38786, + 44661, + 43365, + 33943, + 45011, + 43342, + 29079, + 45286, + 43716, + 24488, + 45637, + 44363, + 20246, + 46042, + 45136, + 16107, + 46557, + 45874, + 11757, + 47879, + 47597, + 9086, + 18377, + 32572, + 59067, + 23205, + 33785, + 58885, + 27156, + 35340, + 58352, + 32689, + 37616, + 58936, + 37652, + 40147, + 58645, + 41914, + 41954, + 57047, + 43874, + 41959, + 53062, + 45847, + 41774, + 48825, + 47093, + 41562, + 43941, + 47635, + 41284, + 38825, + 47983, + 41031, + 33994, + 48150, + 40944, + 29252, + 48333, + 41109, + 24710, + 48614, + 41573, + 20280, + 48935, + 42153, + 15840, + 49217, + 42863, + 11501, + 50204, + 44492, + 8746, + 22878, + 29848, + 59901, + 28308, + 30754, + 59552, + 32716, + 32339, + 59042, + 36242, + 34642, + 59092, + 41803, + 37542, + 59173, + 47051, + 40067, + 58367, + 48177, + 40043, + 54104, + 49603, + 40029, + 49695, + 50223, + 39491, + 44231, + 50624, + 38934, + 39006, + 50837, + 38622, + 34165, + 50911, + 38430, + 29467, + 50985, + 38460, + 24852, + 51138, + 38731, + 20185, + 51341, + 39016, + 15363, + 51608, + 39516, + 10788, + 52032, + 40919, + 8188, + 29050, + 25601, + 61130, + 33399, + 27639, + 60335, + 36863, + 29908, + 60141, + 40991, + 32130, + 59833, + 46605, + 35144, + 60213, + 51516, + 38398, + 59856, + 52343, + 38213, + 55397, + 52818, + 37833, + 50400, + 53263, + 37209, + 44714, + 53477, + 36618, + 39399, + 53522, + 36181, + 34361, + 53553, + 35905, + 29582, + 53543, + 35751, + 24707, + 53655, + 35840, + 19651, + 53787, + 36007, + 14482, + 54045, + 36416, + 9888, + 53256, + 37924, + 8534, + 33602, + 23526, + 62134, + 36531, + 25028, + 60794, + 40529, + 26409, + 59847, + 45064, + 28502, + 60136, + 49202, + 31615, + 59696, + 53385, + 34243, + 59314, + 55110, + 35248, + 56405, + 56327, + 35522, + 51681, + 56627, + 34608, + 45795, + 56600, + 33745, + 39794, + 56173, + 32993, + 34421, + 55892, + 32191, + 29061, + 55395, + 31965, + 23396, + 55120, + 31970, + 17875, + 54876, + 32497, + 12924, + 54235, + 33798, + 10407, + 53532, + 35690, + 9358, + 37375, + 19200, + 61912, + 41608, + 20773, + 60567, + 45154, + 24205, + 59672, + 48424, + 25443, + 59591, + 52594, + 28099, + 59707, + 55279, + 29761, + 58137, + 55545, + 30333, + 55391, + 56482, + 30206, + 50198, + 56794, + 29172, + 43849, + 56698, + 28424, + 37905, + 56268, + 27892, + 32449, + 55899, + 27114, + 26924, + 55893, + 27137, + 21335, + 56657, + 26969, + 15500, + 55446, + 28667, + 12149, + 54258, + 30607, + 10748, + 53685, + 32844, + 9911, + 40193, + 14211, + 61961, + 45467, + 17483, + 60479, + 49257, + 19794, + 60626, + 52195, + 22278, + 60166, + 54583, + 24591, + 58821, + 55364, + 24623, + 56770, + 55717, + 25949, + 55075, + 56717, + 24597, + 48498, + 57027, + 23640, + 42221, + 56863, + 22750, + 36249, + 57025, + 22037, + 31101, + 57403, + 20651, + 25205, + 57658, + 19921, + 19147, + 57081, + 21073, + 14032, + 55532, + 24227, + 12020, + 54182, + 27042, + 10897, + 53739, + 29459, + 10296, + 44125, + 13346, + 59787, + 46713, + 13798, + 60196, + 52276, + 16545, + 61368, + 53538, + 18147, + 59700, + 55475, + 19017, + 56797, + 56084, + 20583, + 56188, + 56420, + 21783, + 53783, + 57395, + 19415, + 47228, + 57508, + 17977, + 40145, + 57169, + 15804, + 33984, + 56638, + 13975, + 28270, + 56397, + 13822, + 23102, + 56175, + 13319, + 17264, + 54939, + 12918, + 12904, + 54923, + 17791, + 12016, + 54566, + 23412, + 11212, + 54144, + 25528, + 10676, + 44009, + 13185, + 57997, + 46123, + 13439, + 58333, + 53674, + 13834, + 60624, + 54969, + 14037, + 56751, + 55326, + 14113, + 56218, + 55819, + 14847, + 55610, + 55983, + 13811, + 50486, + 56123, + 13496, + 44560, + 56143, + 13233, + 38190, + 56159, + 12946, + 33227, + 55716, + 12583, + 28147, + 55538, + 12368, + 23245, + 55781, + 12060, + 17685, + 54441, + 12051, + 12617, + 53862, + 12121, + 12014, + 53497, + 12171, + 11638, + 54125, + 22672, + 10898, + 8706, + 54073, + 57470, + 7905, + 56058, + 58080, + 7983, + 57779, + 57928, + 8259, + 58107, + 57346, + 9436, + 57102, + 54374, + 9911, + 56745, + 51907, + 10311, + 55949, + 49677, + 11546, + 54104, + 44709, + 11925, + 52853, + 40841, + 12140, + 52168, + 37684, + 11946, + 52064, + 33952, + 11919, + 52258, + 29849, + 11417, + 52830, + 24277, + 11020, + 53393, + 12419, + 11067, + 53334, + 11802, + 11113, + 53282, + 11235, + 11162, + 53231, + 10710, + 8989, + 51961, + 57611, + 9240, + 54203, + 57566, + 8406, + 56624, + 58253, + 8515, + 58124, + 58127, + 9289, + 58066, + 56441, + 10217, + 57256, + 53866, + 10383, + 56844, + 51303, + 11632, + 55022, + 46690, + 12045, + 53353, + 42191, + 12131, + 52307, + 38156, + 11942, + 52067, + 33424, + 11866, + 52592, + 27810, + 12092, + 53330, + 21307, + 11467, + 53583, + 12436, + 11512, + 53501, + 11659, + 11558, + 53427, + 10957, + 17590, + 54325, + 9147, + 9337, + 51777, + 57886, + 9470, + 52143, + 57834, + 10033, + 54384, + 57707, + 9223, + 57268, + 58525, + 9554, + 58449, + 58217, + 10409, + 58166, + 55583, + 11484, + 57449, + 52670, + 12113, + 56373, + 49194, + 12470, + 54100, + 43665, + 12319, + 52256, + 38363, + 11999, + 51455, + 32659, + 12081, + 52735, + 25899, + 12395, + 54393, + 16991, + 12279, + 53921, + 12476, + 16534, + 54927, + 10851, + 21088, + 54998, + 10113, + 30569, + 54538, + 8991, + 9422, + 49590, + 58901, + 9729, + 51438, + 58606, + 10223, + 52411, + 58160, + 11330, + 54661, + 57936, + 10737, + 58090, + 58964, + 12459, + 58241, + 57534, + 13182, + 58158, + 54727, + 17054, + 58544, + 52385, + 19808, + 57464, + 47675, + 20360, + 56785, + 42371, + 19987, + 56969, + 35952, + 19888, + 56830, + 27739, + 20050, + 56449, + 19510, + 21193, + 56395, + 12684, + 24357, + 56470, + 10233, + 31307, + 55302, + 9423, + 32688, + 54539, + 8588, + 10079, + 47221, + 59215, + 10463, + 49327, + 59310, + 10838, + 51100, + 59112, + 11565, + 52841, + 58679, + 13956, + 55171, + 58362, + 17183, + 58188, + 58741, + 22210, + 57863, + 56127, + 25374, + 58090, + 53385, + 28609, + 58123, + 50362, + 29763, + 57628, + 45113, + 30332, + 57325, + 39472, + 31191, + 57247, + 32917, + 31902, + 57238, + 26165, + 32153, + 58443, + 15658, + 33220, + 56973, + 11722, + 35233, + 55279, + 9244, + 39167, + 54450, + 8352, + 11208, + 45641, + 58729, + 11735, + 46743, + 59117, + 12300, + 48838, + 59945, + 13897, + 50968, + 59896, + 21499, + 53711, + 59156, + 24384, + 56091, + 58779, + 28388, + 56629, + 56692, + 31172, + 56178, + 53117, + 33108, + 55002, + 48862, + 34150, + 54526, + 43779, + 35163, + 54161, + 38272, + 35885, + 53990, + 32262, + 36751, + 53940, + 26110, + 38023, + 53442, + 19879, + 39181, + 53456, + 13659, + 39907, + 54389, + 8162, + 41328, + 53591, + 6911, + 12016, + 42286, + 58346, + 12711, + 43583, + 58487, + 14559, + 45673, + 59177, + 21811, + 47981, + 59099, + 24983, + 50388, + 58201, + 29331, + 51524, + 56684, + 33328, + 53109, + 55275, + 35532, + 52535, + 51423, + 37198, + 51914, + 47244, + 38783, + 51271, + 42524, + 39847, + 51012, + 37314, + 40721, + 50812, + 31908, + 41333, + 50835, + 26437, + 41758, + 50871, + 21025, + 42387, + 51554, + 15484, + 43121, + 52411, + 10381, + 43891, + 53180, + 5966, + 12606, + 39875, + 58357, + 13829, + 40759, + 58548, + 21331, + 42662, + 58764, + 24538, + 44629, + 58260, + 29250, + 46258, + 57491, + 33516, + 48211, + 55880, + 37540, + 49909, + 54739, + 39688, + 49365, + 50666, + 41965, + 48936, + 46644, + 42991, + 48689, + 41883, + 43848, + 48478, + 36766, + 44452, + 48485, + 31698, + 44783, + 48698, + 26660, + 45080, + 49151, + 21881, + 45568, + 49812, + 17059, + 46085, + 50546, + 12385, + 46597, + 51347, + 8101, + 14736, + 36551, + 58656, + 21290, + 37457, + 57878, + 24125, + 39396, + 58352, + 29573, + 41505, + 58654, + 33973, + 43440, + 58049, + 37778, + 45154, + 56545, + 42157, + 46971, + 54922, + 44241, + 46536, + 50780, + 46017, + 46293, + 46352, + 46802, + 46287, + 41706, + 47315, + 46192, + 36652, + 47637, + 46195, + 31668, + 47867, + 46475, + 26932, + 48148, + 46996, + 22408, + 48470, + 47770, + 18253, + 48814, + 48629, + 13907, + 49126, + 49025, + 9271, + 21217, + 32775, + 58812, + 24324, + 34619, + 58947, + 28940, + 36145, + 58795, + 34026, + 38262, + 58844, + 38705, + 40870, + 58457, + 42844, + 42608, + 57078, + 46810, + 44713, + 55817, + 48390, + 44581, + 51358, + 49504, + 44338, + 46607, + 49964, + 43929, + 41628, + 50265, + 43739, + 36663, + 50434, + 43687, + 31712, + 50584, + 43726, + 27004, + 50768, + 44032, + 22380, + 50984, + 44507, + 17730, + 51229, + 44994, + 13119, + 51539, + 45587, + 8789, + 24357, + 29856, + 59369, + 29017, + 31335, + 59586, + 34659, + 33522, + 59158, + 37658, + 35606, + 59006, + 42954, + 38506, + 59253, + 47282, + 40718, + 58258, + 50727, + 42741, + 56770, + 51761, + 42477, + 52075, + 52406, + 42068, + 46896, + 52883, + 41622, + 41758, + 53054, + 41349, + 36821, + 53094, + 41102, + 31911, + 53182, + 41054, + 27129, + 53240, + 41094, + 22297, + 53452, + 41434, + 17411, + 53475, + 41461, + 12252, + 53756, + 41824, + 7707, + 30026, + 26920, + 60924, + 34967, + 28930, + 60687, + 37028, + 30440, + 60095, + 42241, + 33258, + 59801, + 47342, + 36126, + 60246, + 51963, + 39145, + 60109, + 54055, + 40204, + 57395, + 55309, + 40548, + 53233, + 55699, + 39875, + 47395, + 55717, + 39219, + 41988, + 55625, + 38732, + 36851, + 55551, + 38255, + 31946, + 54865, + 37473, + 26358, + 54502, + 37237, + 20820, + 54197, + 37201, + 15281, + 54214, + 38047, + 11984, + 54026, + 39250, + 9758, + 35131, + 24819, + 61718, + 37757, + 25572, + 60508, + 41099, + 27341, + 59923, + 46120, + 29770, + 60166, + 50179, + 32767, + 59900, + 53684, + 35123, + 58730, + 55228, + 36025, + 56209, + 56256, + 36507, + 52227, + 56595, + 35778, + 46342, + 56589, + 34891, + 40297, + 56137, + 34199, + 34969, + 55842, + 33431, + 29691, + 55252, + 33191, + 24093, + 54958, + 33241, + 18668, + 54604, + 33439, + 13490, + 54294, + 34996, + 11438, + 54054, + 36490, + 10010, + 38565, + 19521, + 61613, + 41561, + 23115, + 60391, + 46034, + 25174, + 59609, + 49177, + 26742, + 59630, + 53370, + 29377, + 59753, + 55054, + 30526, + 57088, + 55478, + 31402, + 55477, + 56405, + 31537, + 50744, + 56542, + 30710, + 44528, + 56654, + 29773, + 38395, + 56228, + 29204, + 33012, + 55813, + 28510, + 27631, + 55795, + 28459, + 22022, + 56403, + 28382, + 16158, + 55385, + 30000, + 12773, + 54846, + 31878, + 11269, + 54130, + 33878, + 10271, + 43017, + 17665, + 60833, + 44857, + 19401, + 60375, + 49938, + 20685, + 60468, + 52343, + 23552, + 60071, + 54990, + 24855, + 58360, + 55355, + 25828, + 56574, + 55602, + 27064, + 55165, + 56652, + 25956, + 49200, + 56990, + 24813, + 42785, + 56981, + 24140, + 36863, + 56958, + 23429, + 31624, + 57209, + 22782, + 25987, + 57382, + 22646, + 20693, + 57439, + 23260, + 14792, + 55965, + 25367, + 12440, + 54595, + 28316, + 11224, + 54112, + 30573, + 10581, + 44596, + 13737, + 60202, + 47875, + 14285, + 60691, + 51950, + 18560, + 60685, + 54593, + 19458, + 58481, + 55720, + 20988, + 56869, + 55304, + 22344, + 55901, + 56120, + 22976, + 54598, + 57352, + 21118, + 48088, + 57680, + 19270, + 40759, + 57660, + 18272, + 34986, + 57968, + 16142, + 29750, + 56709, + 14597, + 23518, + 56355, + 13796, + 17595, + 55338, + 13336, + 13210, + 55391, + 20299, + 12328, + 54912, + 23959, + 11465, + 54043, + 27194, + 10725, + 45074, + 13524, + 58430, + 49132, + 13733, + 59980, + 54222, + 14017, + 60964, + 55049, + 14321, + 56692, + 55454, + 15086, + 56247, + 56070, + 18295, + 55766, + 56865, + 18268, + 53359, + 56212, + 13851, + 44604, + 56256, + 13594, + 38260, + 56372, + 13359, + 33385, + 55967, + 12963, + 28341, + 55777, + 12780, + 23509, + 55956, + 12497, + 17998, + 54729, + 12345, + 12834, + 54085, + 12347, + 12181, + 53678, + 12356, + 11773, + 54427, + 23589, + 11062, + 8881, + 54464, + 57738, + 8310, + 56631, + 58225, + 8415, + 57998, + 58090, + 8903, + 58200, + 57373, + 9858, + 57264, + 54479, + 10293, + 56905, + 52004, + 10605, + 56158, + 49858, + 11733, + 54199, + 44747, + 11999, + 52882, + 40786, + 12176, + 52162, + 37572, + 11946, + 52067, + 33798, + 11785, + 52457, + 29137, + 11537, + 52891, + 24343, + 11295, + 53524, + 12545, + 11330, + 53469, + 11984, + 11364, + 53416, + 11465, + 11400, + 53366, + 10980, + 9219, + 52319, + 57983, + 9373, + 54618, + 57886, + 8882, + 57205, + 58424, + 9023, + 58365, + 58305, + 9921, + 58189, + 56494, + 10653, + 57422, + 53977, + 10736, + 57055, + 51459, + 11875, + 55151, + 46754, + 12148, + 53400, + 42149, + 12168, + 52303, + 38046, + 11910, + 51944, + 33231, + 11932, + 52638, + 27728, + 12321, + 53450, + 21364, + 11757, + 53724, + 12594, + 11786, + 53646, + 11902, + 11817, + 53574, + 11273, + 19592, + 54984, + 9314, + 9662, + 51967, + 58006, + 9831, + 52505, + 58179, + 10090, + 54834, + 58094, + 9765, + 57865, + 58708, + 10204, + 58460, + 58183, + 10916, + 58282, + 55627, + 11899, + 57531, + 52669, + 12375, + 56534, + 49333, + 12771, + 53992, + 43636, + 12067, + 51575, + 37888, + 11993, + 51430, + 32473, + 12569, + 52311, + 25958, + 12709, + 54554, + 16956, + 12534, + 54051, + 12680, + 17625, + 55296, + 11233, + 21629, + 55505, + 10672, + 31708, + 55291, + 9341, + 9727, + 50094, + 59091, + 10305, + 52139, + 58301, + 10746, + 52761, + 58458, + 11236, + 55165, + 58403, + 11338, + 58344, + 58692, + 13003, + 58248, + 57493, + 14616, + 58675, + 55461, + 18190, + 58725, + 52598, + 20860, + 57793, + 48049, + 22499, + 57017, + 43129, + 22280, + 56800, + 36405, + 21822, + 56960, + 28734, + 21412, + 56882, + 20115, + 22465, + 56802, + 12992, + 26593, + 56695, + 11336, + 32665, + 56464, + 10054, + 34178, + 55148, + 9360, + 10480, + 47909, + 59510, + 10955, + 49769, + 59255, + 11281, + 51609, + 59099, + 12273, + 53144, + 58883, + 15329, + 55945, + 58812, + 20402, + 57682, + 57994, + 23663, + 57742, + 56115, + 26772, + 58047, + 53380, + 29419, + 58900, + 50700, + 30132, + 58449, + 45637, + 30837, + 58232, + 39582, + 32208, + 57078, + 33053, + 31793, + 58355, + 25146, + 33258, + 57742, + 17958, + 34836, + 57405, + 13037, + 38213, + 55937, + 9541, + 40445, + 55540, + 9008, + 11547, + 46264, + 58964, + 12183, + 46948, + 59238, + 12754, + 49272, + 59877, + 21012, + 51898, + 58706, + 22743, + 54131, + 59276, + 25726, + 56067, + 58318, + 30016, + 56169, + 56093, + 32887, + 57066, + 53954, + 35848, + 58170, + 51744, + 37230, + 56825, + 46251, + 38213, + 56567, + 40860, + 39118, + 56199, + 34929, + 39868, + 55779, + 28658, + 40537, + 55348, + 22394, + 41767, + 54886, + 16063, + 42227, + 56312, + 10202, + 42783, + 54922, + 7963, + 12480, + 42782, + 58306, + 12971, + 44115, + 58384, + 20101, + 46882, + 59118, + 23678, + 48607, + 58926, + 25848, + 50635, + 57873, + 30550, + 51738, + 56292, + 34287, + 53649, + 55255, + 38194, + 55422, + 54038, + 40424, + 54176, + 49945, + 41395, + 54123, + 45353, + 42231, + 53952, + 40223, + 43047, + 53728, + 34703, + 43617, + 53720, + 29019, + 44161, + 53564, + 23370, + 44748, + 53722, + 17607, + 45399, + 54705, + 12172, + 45836, + 54590, + 9476, + 13358, + 39812, + 57876, + 16471, + 41067, + 58139, + 23147, + 43573, + 58776, + 26126, + 45663, + 58692, + 30118, + 46534, + 56914, + 34539, + 48795, + 55447, + 38473, + 50350, + 54724, + 42448, + 52044, + 53356, + 44228, + 51704, + 49207, + 45560, + 51482, + 44714, + 46369, + 51292, + 39706, + 46890, + 51278, + 34627, + 47234, + 51478, + 29379, + 47450, + 51961, + 24205, + 47890, + 52370, + 19094, + 48301, + 53073, + 14366, + 48769, + 53802, + 9913, + 16791, + 37070, + 58514, + 23286, + 38291, + 58122, + 25243, + 40134, + 58510, + 30645, + 42389, + 58899, + 34754, + 43714, + 57325, + 39015, + 45673, + 55817, + 42873, + 47529, + 54737, + 46761, + 49288, + 53300, + 48732, + 49023, + 49072, + 49380, + 49164, + 44359, + 49780, + 49108, + 39385, + 50033, + 49162, + 34393, + 50272, + 49510, + 29539, + 50521, + 50002, + 24777, + 50761, + 50661, + 20474, + 50965, + 51125, + 15798, + 51263, + 51452, + 11025, + 23108, + 33693, + 58878, + 25835, + 34967, + 58217, + 30484, + 36848, + 58740, + 35454, + 39236, + 58789, + 39580, + 41369, + 58000, + 43657, + 43224, + 56463, + 47317, + 45141, + 55374, + 50356, + 46909, + 53484, + 51587, + 46899, + 49061, + 52163, + 46704, + 44224, + 52564, + 46599, + 39305, + 52763, + 46656, + 34426, + 52936, + 46747, + 29542, + 53108, + 47043, + 24774, + 53277, + 47441, + 20050, + 53454, + 47820, + 15329, + 53765, + 48405, + 10888, + 27656, + 30883, + 59679, + 30162, + 32252, + 59594, + 35112, + 34315, + 59065, + 39006, + 36536, + 58970, + 44697, + 39540, + 59042, + 47671, + 41240, + 57838, + 51370, + 43275, + 56235, + 54106, + 45015, + 54509, + 54498, + 44534, + 49312, + 55201, + 44282, + 44465, + 55336, + 43990, + 39425, + 55274, + 43720, + 34336, + 55363, + 43562, + 29438, + 54830, + 43105, + 24043, + 54499, + 43058, + 18683, + 54090, + 42974, + 13530, + 54087, + 44374, + 11261, + 32039, + 27765, + 60459, + 35319, + 29982, + 60518, + 38135, + 31347, + 60021, + 43339, + 34256, + 59814, + 48367, + 37121, + 60211, + 52172, + 39681, + 59514, + 54481, + 40771, + 56566, + 55975, + 41990, + 54455, + 57013, + 41748, + 49234, + 56797, + 40921, + 43512, + 56162, + 40035, + 37852, + 55532, + 39141, + 32411, + 54826, + 38439, + 26919, + 54427, + 38227, + 21463, + 54144, + 38109, + 15903, + 54186, + 39060, + 12743, + 54096, + 40296, + 11130, + 35544, + 25118, + 61594, + 39018, + 27171, + 60164, + 42174, + 28316, + 59972, + 46637, + 31269, + 59647, + 51052, + 33855, + 60069, + 54159, + 35770, + 57988, + 55256, + 36816, + 56100, + 56150, + 37628, + 52609, + 56635, + 36877, + 46953, + 56519, + 36093, + 40957, + 56034, + 35310, + 35422, + 55673, + 34606, + 30158, + 55163, + 34077, + 24572, + 54777, + 34022, + 19170, + 54477, + 34324, + 13753, + 54174, + 35986, + 12091, + 54237, + 37164, + 11138, + 39653, + 21170, + 61128, + 42612, + 25078, + 59727, + 46743, + 25485, + 59588, + 49770, + 28113, + 59726, + 54144, + 30744, + 59818, + 55100, + 31474, + 56764, + 55315, + 32518, + 55390, + 56290, + 32771, + 51360, + 56549, + 32022, + 45281, + 56688, + 30879, + 39221, + 56198, + 30102, + 33372, + 56017, + 29576, + 28109, + 55741, + 29697, + 22830, + 55478, + 29979, + 17382, + 55120, + 31058, + 13283, + 54644, + 32903, + 11880, + 54291, + 34891, + 10805, + 42910, + 19228, + 60761, + 44874, + 20140, + 60603, + 49238, + 23800, + 59732, + 52980, + 25175, + 59793, + 55363, + 25488, + 57964, + 55347, + 26834, + 56387, + 55526, + 28274, + 55195, + 56562, + 27513, + 49989, + 56849, + 25724, + 42588, + 56960, + 25058, + 37188, + 56899, + 24686, + 32034, + 56087, + 24717, + 26477, + 57019, + 24156, + 21134, + 57097, + 25502, + 15549, + 56622, + 26886, + 12896, + 55023, + 29596, + 11569, + 54499, + 31697, + 10885, + 45066, + 14129, + 60617, + 47542, + 16323, + 60198, + 52701, + 19757, + 60512, + 55115, + 21493, + 57842, + 54878, + 22288, + 56479, + 55549, + 23939, + 56144, + 55876, + 23880, + 55234, + 56749, + 22743, + 48508, + 57015, + 21229, + 41478, + 57550, + 19487, + 35627, + 57894, + 18506, + 30550, + 57272, + 16059, + 24214, + 56724, + 15825, + 18985, + 56064, + 14401, + 13661, + 55937, + 23029, + 12583, + 55148, + 25072, + 11676, + 54336, + 28380, + 10927, + 47654, + 13691, + 59736, + 53517, + 14927, + 61523, + 55401, + 14484, + 58932, + 55193, + 15249, + 56692, + 55758, + 18551, + 56333, + 56108, + 19261, + 55833, + 57065, + 19383, + 53615, + 56878, + 17122, + 46353, + 56353, + 13900, + 38303, + 56490, + 13701, + 33467, + 56193, + 13308, + 28510, + 55992, + 13154, + 23750, + 56110, + 12885, + 18285, + 55013, + 12642, + 13051, + 54307, + 12576, + 12347, + 54859, + 20341, + 11799, + 54658, + 23853, + 11241, + 9217, + 54611, + 57837, + 8689, + 57184, + 58367, + 8805, + 58189, + 58228, + 9472, + 58221, + 57341, + 10216, + 57399, + 54564, + 10611, + 57036, + 52079, + 10852, + 56332, + 50012, + 11885, + 54273, + 44770, + 12058, + 52898, + 40723, + 12072, + 52256, + 37128, + 11946, + 52069, + 33642, + 11814, + 52475, + 29150, + 11636, + 52939, + 24395, + 11520, + 53633, + 12650, + 11547, + 53577, + 12136, + 11573, + 53526, + 11655, + 11602, + 53478, + 11206, + 9540, + 52819, + 57901, + 9742, + 54779, + 57991, + 9303, + 57770, + 58576, + 9522, + 58433, + 58324, + 10438, + 58286, + 56534, + 11009, + 57552, + 54060, + 11022, + 57222, + 51583, + 12066, + 55245, + 46793, + 12275, + 53465, + 42068, + 12187, + 52286, + 37928, + 11953, + 52037, + 33083, + 11665, + 52642, + 27569, + 12501, + 53547, + 21395, + 11984, + 53832, + 12716, + 12003, + 53758, + 12094, + 12022, + 53690, + 11522, + 20750, + 55574, + 9463, + 9953, + 52136, + 58110, + 10237, + 52988, + 58208, + 10493, + 55011, + 58200, + 10246, + 58308, + 58665, + 10712, + 58464, + 58152, + 11307, + 58367, + 55653, + 12183, + 57629, + 52719, + 12569, + 56658, + 49446, + 12942, + 54166, + 43858, + 12730, + 53107, + 38426, + 11986, + 51414, + 32290, + 12649, + 52322, + 25851, + 12941, + 54674, + 16884, + 12775, + 53798, + 12925, + 18728, + 55684, + 11459, + 22361, + 55971, + 10299, + 32775, + 56030, + 9706, + 10122, + 50573, + 59048, + 10623, + 52326, + 58399, + 11174, + 53246, + 58479, + 11143, + 56144, + 58995, + 11780, + 58355, + 58578, + 13395, + 58251, + 57458, + 15644, + 58789, + 55515, + 21307, + 58421, + 52558, + 23360, + 58067, + 48631, + 23870, + 57401, + 43558, + 24440, + 57218, + 37072, + 23152, + 57353, + 29392, + 23779, + 57175, + 21404, + 23495, + 56525, + 14347, + 29356, + 57407, + 11881, + 33722, + 56971, + 10895, + 35443, + 56013, + 9972, + 10858, + 48553, + 59742, + 11121, + 50179, + 59504, + 11641, + 52106, + 59071, + 13429, + 53422, + 58504, + 19379, + 56723, + 59023, + 22581, + 57282, + 57435, + 25353, + 57199, + 55730, + 28956, + 57538, + 53119, + 30575, + 58329, + 50447, + 31219, + 58364, + 45695, + 32678, + 57217, + 39852, + 33434, + 57017, + 33480, + 33383, + 58145, + 25861, + 35486, + 57382, + 19860, + 36374, + 57108, + 14528, + 39567, + 56259, + 10957, + 41734, + 56610, + 9683, + 11932, + 46599, + 59095, + 12343, + 47405, + 59491, + 13090, + 49710, + 59803, + 22931, + 52316, + 58786, + 23758, + 54148, + 58923, + 27732, + 55889, + 57715, + 31320, + 56194, + 56087, + 34043, + 57036, + 54074, + 36675, + 58004, + 51427, + 37998, + 57352, + 46702, + 39015, + 56905, + 41423, + 39562, + 56735, + 35316, + 40540, + 56431, + 29255, + 41473, + 56243, + 23104, + 42431, + 55585, + 16837, + 43497, + 56043, + 12151, + 44015, + 55547, + 9505, + 12692, + 43298, + 58454, + 13813, + 44967, + 58894, + 21336, + 47071, + 59117, + 24177, + 48985, + 58663, + 27637, + 50723, + 57268, + 32170, + 51832, + 55840, + 35597, + 53953, + 54904, + 39172, + 54850, + 54091, + 42995, + 56863, + 52662, + 44178, + 56627, + 47909, + 45070, + 56388, + 42999, + 45652, + 56276, + 37645, + 46179, + 56342, + 32199, + 46184, + 55287, + 25304, + 46375, + 54710, + 19105, + 46695, + 55541, + 13595, + 47152, + 55277, + 11210, + 13282, + 40660, + 58521, + 19974, + 42123, + 58455, + 23659, + 44285, + 58578, + 27914, + 46019, + 58059, + 31069, + 47021, + 56584, + 35285, + 49295, + 55127, + 39483, + 50666, + 54412, + 43505, + 52518, + 52929, + 46745, + 54190, + 51727, + 48050, + 54237, + 47455, + 48622, + 54222, + 42548, + 49236, + 54123, + 37468, + 49531, + 54368, + 32167, + 49983, + 54684, + 26629, + 50398, + 55076, + 21467, + 50240, + 55161, + 16158, + 50318, + 54720, + 12050, + 19517, + 37755, + 58485, + 23811, + 39004, + 58268, + 27517, + 41056, + 58679, + 32184, + 42832, + 58417, + 35684, + 44201, + 56916, + 39725, + 46230, + 55635, + 43480, + 48066, + 54475, + 47886, + 49871, + 52813, + 51630, + 51630, + 51773, + 51564, + 51868, + 47038, + 51944, + 51957, + 42141, + 52321, + 52130, + 37200, + 52569, + 52450, + 32244, + 52860, + 52916, + 27223, + 52963, + 53368, + 22541, + 53169, + 53813, + 17905, + 53412, + 54138, + 13184, + 23448, + 34447, + 58916, + 27282, + 35558, + 58373, + 32737, + 37727, + 58926, + 36312, + 40045, + 58852, + 40507, + 41959, + 57630, + 44467, + 43828, + 56101, + 47797, + 45774, + 54855, + 51246, + 47525, + 52836, + 53342, + 49077, + 51058, + 54699, + 49457, + 46880, + 55108, + 49536, + 42008, + 55170, + 49569, + 37061, + 55294, + 49730, + 32054, + 54862, + 49421, + 26656, + 54401, + 49347, + 21447, + 53975, + 49148, + 16159, + 53733, + 49770, + 12497, + 28124, + 31349, + 59457, + 33395, + 33165, + 59170, + 35464, + 35131, + 59044, + 40365, + 37557, + 59061, + 45361, + 40215, + 58553, + 48249, + 41838, + 57188, + 52079, + 43885, + 55760, + 53878, + 45589, + 54131, + 56388, + 46853, + 51396, + 57050, + 46568, + 46483, + 56622, + 45956, + 40993, + 55792, + 45021, + 35220, + 55319, + 44389, + 29816, + 54805, + 43955, + 24450, + 54507, + 44013, + 19157, + 54239, + 43986, + 13978, + 54026, + 45661, + 12294, + 33216, + 29017, + 60265, + 35591, + 30694, + 60292, + 38942, + 32332, + 59932, + 44625, + 35456, + 59809, + 49295, + 38191, + 60269, + 52773, + 40365, + 58879, + 54688, + 41421, + 56374, + 55652, + 42809, + 54798, + 57040, + 42754, + 49930, + 56859, + 41864, + 44081, + 56384, + 41023, + 38481, + 55642, + 40043, + 32849, + 55243, + 39584, + 27538, + 54608, + 39076, + 22106, + 54376, + 39261, + 16983, + 54161, + 39919, + 13136, + 54106, + 41263, + 11873, + 36348, + 25591, + 60773, + 39291, + 27920, + 60223, + 43100, + 29022, + 59957, + 47515, + 32397, + 59835, + 52117, + 35078, + 60179, + 54593, + 36494, + 57247, + 55170, + 37765, + 55804, + 56072, + 38637, + 53292, + 56496, + 38047, + 47426, + 56570, + 37179, + 41672, + 56067, + 36268, + 35914, + 55644, + 35794, + 30791, + 54999, + 35110, + 25205, + 54668, + 34969, + 19661, + 54334, + 35368, + 14410, + 54087, + 36853, + 12488, + 54199, + 38099, + 11725, + 40635, + 24362, + 60531, + 43395, + 25346, + 59681, + 46896, + 25962, + 59656, + 50379, + 29188, + 59495, + 54190, + 31651, + 59418, + 54992, + 32377, + 56416, + 55325, + 33486, + 55492, + 56274, + 34013, + 52033, + 56557, + 32771, + 45587, + 56714, + 32163, + 39871, + 56295, + 31262, + 33975, + 55971, + 30816, + 28714, + 55622, + 30748, + 23277, + 55230, + 31436, + 18310, + 54961, + 32054, + 13524, + 54509, + 33848, + 12288, + 54174, + 35825, + 11337, + 43110, + 19533, + 60696, + 46802, + 23763, + 59714, + 49909, + 24947, + 59820, + 53581, + 25509, + 59814, + 55146, + 26587, + 57225, + 55295, + 27911, + 56238, + 55505, + 29027, + 55229, + 56498, + 28913, + 50614, + 56823, + 26967, + 43153, + 56795, + 26401, + 37481, + 56662, + 26162, + 32583, + 55986, + 26219, + 27135, + 56899, + 25513, + 21558, + 56766, + 26458, + 16057, + 55676, + 28367, + 13144, + 55158, + 30657, + 12041, + 54646, + 32773, + 11210, + 45749, + 14794, + 61314, + 51071, + 19404, + 60789, + 52596, + 20619, + 60274, + 55040, + 21931, + 56979, + 55239, + 24002, + 56737, + 55599, + 24203, + 56184, + 55741, + 24982, + 55236, + 56690, + 24209, + 49415, + 57053, + 22316, + 42151, + 56880, + 21431, + 36270, + 57102, + 20649, + 31214, + 57850, + 19138, + 25684, + 57445, + 18883, + 20533, + 56914, + 19183, + 14255, + 56473, + 24585, + 12898, + 55581, + 26449, + 11917, + 54627, + 29356, + 11147, + 48061, + 14051, + 60086, + 53777, + 15960, + 61522, + 55410, + 15319, + 58641, + 55530, + 18733, + 56739, + 55833, + 19616, + 56366, + 56247, + 20826, + 55979, + 56334, + 22085, + 54037, + 57230, + 18972, + 47614, + 56817, + 15483, + 38980, + 56591, + 14001, + 33528, + 56397, + 13619, + 28656, + 56186, + 13498, + 23969, + 56046, + 13706, + 19290, + 55296, + 12938, + 13267, + 54526, + 12803, + 12515, + 55312, + 23410, + 11918, + 54825, + 25078, + 11364 + ], + "gridPoints": [ + 17, + 17, + 17 + ], + "precision": 2 + }, + "inputChannels": 3, + "mCurves": [ + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + }, + { + "functionType": 0, + "params": [ + 1.52587890625e-05 + ], + "type": "ParametricCurve" + } + ], + "matrix": { + "e": [ + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0 + ] + }, + "outputChannels": 3, + "type": "lutBtoAType" + } + } + }, + { + "BToA1Tag": { + "data": { + "aCurves": [ + { + "functionType": 4, + "params": [ + 0.0, + 0.00018310546875, + -6.103515625e-05, + 0.0020904541015625, + 0.0, + 0.0, + -0.0008392333984375 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 0.0, + 9.1552734375e-05, + -3.0517578125e-05, + 0.00115966796875, + 0.0, + 0.0, + -0.0003814697265625 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 0.0, + 4.57763671875e-05, + 0.0, + 0.00067138671875, + 0.0, + 0.0, + -9.1552734375e-05 + ], + "type": "ParametricCurve" + } + ], + "bCurves": [ + { + "functionType": 4, + "params": [ + 1.52587890625e-05, + 1.52587890625e-05, + 0.0, + 1.52587890625e-05, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 1.52587890625e-05, + 1.52587890625e-05, + 0.0, + 1.52587890625e-05, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 1.52587890625e-05, + 1.52587890625e-05, + 0.0, + 1.52587890625e-05, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + } + ], + "clut": { + "data": [ + 26352, + 21904, + 8793, + 20218, + 22652, + 62771, + 6135, + 64787, + 0, + 0, + 65535, + 53979, + 65535, + 0, + 11556, + 59400, + 748, + 65535, + 45317, + 42883, + 2763, + 39183, + 43631, + 56742 + ], + "gridPoints": [ + 2, + 2, + 2 + ], + "precision": 2 + }, + "inputChannels": 3, + "mCurves": [ + { + "functionType": 4, + "params": [ + 4.57763671875e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 4.57763671875e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + }, + { + "functionType": 4, + "params": [ + 4.57763671875e-05, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "type": "ParametricCurve" + } + ], + "matrix": { + "e": [ + 1.0, + 0.5915985107421875, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + -1.47900390625, + -0.296966552734375, + 0.0, + 0.742401123046875 + ] + }, + "outputChannels": 3, + "type": "lutBtoAType" + } + } + }, + { + "perceptualRenderingIntentGamutTag": { + "data": { + "signature": "prmg", + "type": "signatureType" + } + } + }, + { + "mediaWhitePointTag": { + "data": { + "XYZ": [ + [ + 0.964202880859375, + 1.0, + 0.8249053955078125 + ] + ], + "type": "XYZArrayType" + } + } + }, + { + "copyrightTag": { + "data": { + "localizedStrings": [ + { + "country": "US", + "language": "en", + "text": "Copyright 2007 International Color Consortium" + } + ], + "type": "multiLocalizedUnicodeType" + } + } + }, + { + "chromaticAdaptationTag": { + "data": { + "type": "s15Fixed16ArrayType", + "values": [ + 1.0480194091796875, + 0.02301025390625, + -0.0501708984375, + 0.02972412109375, + 0.9903411865234375, + -0.0170745849609375, + -0.0092315673828125, + 0.010284423828125, + 0.75213623046875 + ] + } + } + } + ] + } +} \ No newline at end of file diff --git a/Tools/wxWidget/wxProfileDump/wxProfileDump.cpp b/Tools/wxWidget/wxProfileDump/wxProfileDump.cpp index d50f8bc05..bfe9f8665 100644 --- a/Tools/wxWidget/wxProfileDump/wxProfileDump.cpp +++ b/Tools/wxWidget/wxProfileDump/wxProfileDump.cpp @@ -491,7 +491,7 @@ MyChild::MyChild(wxMDIParentFrame *parent, const wxString& title, CIccProfile *p sizerBox->Add(CreateSizerWithText(_("Spectral PCS:"), &m_textSpectralPCS), wxSizerFlags().Expand().Border(wxALL, 0)); sizerBox->Add(CreateSizerWithText(_("Spectral PCS Range:"), &m_textSpectralWavelengths), wxSizerFlags().Expand().Border(wxALL, 0)); sizerBox->Add(CreateSizerWithText(_("BiSpectral PCS Range:"), &m_textBiSpectralWavelengths), wxSizerFlags().Expand().Border(wxALL, 0)); - sizerBox->Add(CreateSizerWithText(_("MCS Color Space:"), &m_textMaterialColorSpace), wxSizerFlags().Expand().Border(wxALL, 0)); + sizerBox->Add(CreateSizerWithText(_("MCS Color Space:"), &m_textMultiplexColorSpace), wxSizerFlags().Expand().Border(wxALL, 0)); sizerTop->Add(sizerBox, wxSizerFlags().Expand().Border(wxALL, 5)); @@ -593,10 +593,10 @@ MyChild::MyChild(wxMDIParentFrame *parent, const wxString& title, CIccProfile *p } if (pHdr->mcs) { - m_textMaterialColorSpace->SetLabel(Fmt.GetColorSpaceSigName((icColorSpaceSignature)pHdr->mcs)); + m_textMultiplexColorSpace->SetLabel(Fmt.GetColorSpaceSigName((icColorSpaceSignature)pHdr->mcs)); } else { - m_textMaterialColorSpace->SetLabel(_T("Not Defined")); + m_textMultiplexColorSpace->SetLabel(_T("Not Defined")); } TagEntryList::iterator i, j; diff --git a/Tools/wxWidget/wxProfileDump/wxProfileDump.h b/Tools/wxWidget/wxProfileDump/wxProfileDump.h index 8a2dd2b3d..de9bccc98 100644 --- a/Tools/wxWidget/wxProfileDump/wxProfileDump.h +++ b/Tools/wxWidget/wxProfileDump/wxProfileDump.h @@ -171,7 +171,7 @@ class MyChild: public wxMDIChildFrame wxStaticText *m_textRenderingIntent; wxStaticText *m_textSpectralPCS; wxStaticText *m_textSpectralWavelengths; - wxStaticText *m_textMaterialColorSpace; + wxStaticText *m_textMultiplexColorSpace; wxStaticText *m_textSize; wxStaticText *m_textClass; wxStaticText *m_textSubClass; diff --git a/docs/icc-profile.schema.json b/docs/icc-profile.schema.json new file mode 100644 index 000000000..8c2465f16 --- /dev/null +++ b/docs/icc-profile.schema.json @@ -0,0 +1,1038 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "icc-profile.schema.json", + "title": "ICC Profile JSON", + "description": "JSON representation of an ICC color profile as produced and consumed by the IccJSON library (IccJSON/IccLibJSON). The outermost wrapper key is 'IccProfile'.", + + "type": "object", + "required": ["IccProfile"], + "properties": { + "IccProfile": { "$ref": "#/$defs/Profile" } + }, + + "$defs": { + + "Signature": { + "type": "string", + "description": "ICC signature in one of four forms: empty string (zero/unset), 4 printable ASCII characters (e.g. 'RGB ', 'XYZ '), 6 characters where the first 2 are any character followed by 4 hex digits (e.g. 'nc1234'), or 8 hex digits (e.g. '00FF0102').", + "pattern": "^(|.{4}|.{2}[0-9a-fA-F]{4}|[0-9a-fA-F]{8})$" + }, + + "HexString": { + "type": "string", + "pattern": "^[0-9a-fA-F]*$", + "description": "Even-length lowercase hex string encoding binary data" + }, + + "DateTime": { + "type": "string", + "description": "ISO-8601 date/time: YYYY-MM-DDTHH:MM:SS" + }, + + "XYZTriplet": { + "type": "array", + "description": "CIE XYZ tristimulus values [X, Y, Z]", + "items": { "type": "number" }, + "minItems": 3, + "maxItems": 3 + }, + + "SpectralRange": { + "type": "object", + "description": "Spectral wavelength range. Used in the profile header (SpectralRange, BiSpectralRange) and inside MPE spectral elements (wavelengths).", + "required": ["start", "end", "steps"], + "properties": { + "start": { "type": "number", "description": "Start wavelength in nm" }, + "end": { "type": "number", "description": "End wavelength in nm" }, + "steps": { "type": "integer", "minimum": 1, "description": "Number of wavelength steps" } + } + }, + + "Profile": { + "type": "object", + "description": "ICC profile object containing a header and a tag list", + "required": ["Header", "Tags"], + "properties": { + "Header": { "$ref": "#/$defs/Header" }, + "Tags": { "$ref": "#/$defs/TagsArray" } + }, + "additionalProperties": false + }, + + "Header": { + "type": "object", + "description": "ICC profile header fields", + "required": ["ProfileVersion", "ProfileDeviceClass", "DataColourSpace", "PCS", + "CreationDateTime", "ProfileFileSignature", "PrimaryPlatform", + "RenderingIntent", "PCSIlluminant"], + "properties": { + "PreferredCMMType": { "$ref": "#/$defs/Signature" }, + "ProfileVersion": { "type": "string", "description": "Version string (e.g. '4.4', '5.0')" }, + "ProfileSubClassVersion":{ "type": "string", "description": "Sub-class version string (omitted when zero)" }, + "ProfileDeviceClass": { "$ref": "#/$defs/Signature", "description": "'scnr', 'prtr', 'mntr', 'link', 'spac', 'abst', 'nmcl'" }, + "ProfileDeviceSubClass": { "$ref": "#/$defs/Signature" }, + "DataColourSpace": { "$ref": "#/$defs/Signature", "description": "Color space signature (e.g. 'RGB ', 'CMYK', 'Lab ')" }, + "PCS": { "$ref": "#/$defs/Signature", "description": "Profile connection space: 'XYZ ' or 'Lab '" }, + "CreationDateTime": { "$ref": "#/$defs/DateTime" }, + "ProfileFileSignature": { "type": "string", "const": "acsp" }, + "PrimaryPlatform": { "$ref": "#/$defs/Signature", "description": "'APPL', 'MSFT', 'SGI ', 'SUNW'" }, + "CMMFlags": { "$ref": "#/$defs/CMMFlags" }, + "DeviceManufacturer": { "$ref": "#/$defs/Signature" }, + "DeviceModel": { "$ref": "#/$defs/Signature" }, + "DeviceAttributes": { "$ref": "#/$defs/DeviceAttributes" }, + "RenderingIntent": { "type": "string", "description": "Rendering intent name (e.g. 'Perceptual', 'Relative Colorimetric')" }, + "PCSIlluminant": { "$ref": "#/$defs/XYZTriplet" }, + "ProfileCreator": { "$ref": "#/$defs/Signature" }, + "ProfileID": { "$ref": "#/$defs/HexString", "minLength": 32, "maxLength": 32, "description": "MD5 profile ID as 32 hex chars (omitted if all-zero)" }, + "SpectralPCS": { "$ref": "#/$defs/Signature", "description": "Spectral PCS signature (ICC v5)" }, + "SpectralRange": { "$ref": "#/$defs/SpectralRange" }, + "BiSpectralRange": { "$ref": "#/$defs/SpectralRange" }, + "MCS": { "$ref": "#/$defs/Signature", "description": "Multiplex connection space (ICC v5)" }, + "ProfileSubClass": { "$ref": "#/$defs/Signature" } + } + }, + + "CMMFlags": { + "type": "object", + "description": "CMM flags decomposed into named bits", + "properties": { + "EmbeddedInFile": { "type": "boolean" }, + "UseWithEmbeddedDataOnly": { "type": "boolean" }, + "ExtendedRangePCS": { "type": "boolean" }, + "MCSNeedsSubset": { "type": "boolean" }, + "VendorFlags": { "$ref": "#/$defs/HexString", "description": "Remaining vendor-specific flag bits as 8 hex chars" } + } + }, + + "DeviceAttributes": { + "type": "object", + "description": "Device attribute flags decomposed into named fields", + "properties": { + "ReflectiveOrTransparency": { "type": "string", "enum": ["reflective", "transparency"] }, + "GlossyOrMatte": { "type": "string", "enum": ["glossy", "matte"] }, + "MediaPolarity": { "type": "string", "enum": ["positive", "negative"] }, + "MediaColour": { "type": "string", "enum": ["colour", "blackAndWhite"] }, + "VendorSpecific": { "$ref": "#/$defs/HexString", "description": "Remaining vendor-specific bits as 16 hex chars" } + } + }, + + "TagsArray": { + "type": "array", + "description": "Ordered list of profile tags. Each element is a single-key object { tagName: tagValue }. Known tags use their ICC name (e.g. 'redMatrixColumnTag'). Private tags use 'PrivateTag_N' with a 'sig' field.", + "items": { "$ref": "#/$defs/TagEntry" } + }, + + "TagEntry": { + "type": "object", + "description": "Single-key object mapping a tag name to its value", + "minProperties": 1, + "maxProperties": 1, + "additionalProperties": { "$ref": "#/$defs/TagValue" } + }, + + "TagValue": { + "type": "object", + "description": "Tag wrapper containing type data, optional Reserved word, and optional alias pointer", + "properties": { + "sig": { "$ref": "#/$defs/Signature", "description": "Raw signature (private tags only)" }, + "Reserved": { "type": "integer", "minimum": 0, "description": "Tag reserved word (emitted only when non-zero)" }, + "sameAs": { "type": "string", "description": "Name of the tag that owns the shared tag object (aliased tags)" }, + "data": { "$ref": "#/$defs/TagTypeData" } + } + }, + + "TagTypeData": { + "type": "object", + "description": "Flat tag data object. Contains a 'type' discriminator field set to the ICC tag type name (from g_icTagTypeNameTable), followed by type-specific fields at the same level.", + "required": ["type"], + "properties": { + "type": { "type": "string", "description": "ICC tag type name" } + }, + "oneOf": [ + { "allOf": [{ "properties": { "type": { "const": "signatureType" } } }, { "$ref": "#/$defs/SignatureTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "textType" } } }, { "$ref": "#/$defs/TextTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "utf8Type" } } }, { "$ref": "#/$defs/TextTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "utf16Type" } } }, { "$ref": "#/$defs/TextTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "utf8ZipType" } } }, { "$ref": "#/$defs/TextTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "zipXmlType" } } }, { "$ref": "#/$defs/TextTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "textDescriptionType" } } }, { "$ref": "#/$defs/TextDescriptionTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "curveType" } } }, { "$ref": "#/$defs/CurveTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "parametricCurveType" } } }, { "$ref": "#/$defs/ParametricCurveTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "segmentedCurveType" } } }, { "$ref": "#/$defs/SegmentedCurveObject" }] }, + { "allOf": [{ "properties": { "type": { "const": "XYZArrayType" } } }, { "$ref": "#/$defs/XYZArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "s15Fixed16ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "u16Fixed16ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "uInt8ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "uInt16ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "uInt32ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "uInt64ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "float16ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "float32ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "float64ArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "sparseMatrixArrayType" } } }, { "$ref": "#/$defs/NumericArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "chromaticityType" } } }, { "$ref": "#/$defs/ChromaticityTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "cicpType" } } }, { "$ref": "#/$defs/CicpTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "colorantOrderType" } } }, { "$ref": "#/$defs/ColorantOrderTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "colorantTableType" } } }, { "$ref": "#/$defs/ColorantTableTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "dataType" } } }, { "$ref": "#/$defs/DataTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "dateTimeType" } } }, { "$ref": "#/$defs/DateTimeTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "measurementType" } } }, { "$ref": "#/$defs/MeasurementTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "viewingConditionsType" } } }, { "$ref": "#/$defs/ViewingConditionsTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "spectralDataInfoType" } } }, { "$ref": "#/$defs/SpectralDataInfoTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "spectralViewingConditionsType"} } }, { "$ref": "#/$defs/SpectralViewingConditionsTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "multiLocalizedUnicodeType" } } }, { "$ref": "#/$defs/MultiLocalizedUnicodeTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "namedColor2Type" } } }, { "$ref": "#/$defs/NamedColor2TypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "profileSequenceDescType" } } }, { "$ref": "#/$defs/ProfileSequenceDescTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "profileSequenceIdentifierType"} } }, { "$ref": "#/$defs/ProfileSequenceIdentifierTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "responseCurveSet16Type" } } }, { "$ref": "#/$defs/ResponseCurveSet16TypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "lut8Type" } } }, { "$ref": "#/$defs/LutMBBTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "lut16Type" } } }, { "$ref": "#/$defs/LutMBBTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "lutAtoBType" } } }, { "$ref": "#/$defs/LutMBBTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "lutBtoAType" } } }, { "$ref": "#/$defs/LutMBBTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "multiProcessElementType" } } }, { "$ref": "#/$defs/MultiProcessElementTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "dictType" } } }, { "$ref": "#/$defs/DictTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "tagStructType" } } }, { "$ref": "#/$defs/TagStructTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "tagArrayType" } } }, { "$ref": "#/$defs/TagArrayTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "embeddedHeightImageType" } } }, { "$ref": "#/$defs/EmbeddedHeightImageTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "embeddedNormalImageType" } } }, { "$ref": "#/$defs/EmbeddedNormalImageTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "embeddedProfileType" } } }, { "$ref": "#/$defs/EmbeddedProfileTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "gamutBoundaryDescType" } } }, { "$ref": "#/$defs/GamutBoundaryDescTypeContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "PrivateType" } } }, { "$ref": "#/$defs/PrivateTypeContent" }] } + ] + }, + + "SignatureTypeContent": { + "type": "object", + "description": "signatureType — stores a 4-char ICC signature", + "properties": { + "signature": { "$ref": "#/$defs/Signature" } + }, + "required": ["signature"] + }, + + "TextTypeContent": { + "type": "object", + "description": "textType / utf8Type / utf16Type / utf8ZipType / zipXmlType", + "properties": { + "text": { "type": "string" } + }, + "required": ["text"] + }, + + "TextDescriptionTypeContent": { + "type": "object", + "description": "textDescriptionType (legacy ICC v2)", + "properties": { + "description": { "type": "string" } + }, + "required": ["description"] + }, + + "CurveTypeContent": { + "type": "object", + "description": "curveType — identity, gamma, or table", + "required": ["curveType"], + "properties": { + "curveType": { "type": "string", "enum": ["identity", "gamma", "table"] }, + "gamma": { "type": "number", "description": "Gamma exponent (when curveType is 'gamma')" }, + "precision": { "type": "integer", "enum": [1, 2], "description": "Table encoding: 1 = 8-bit integers (0–255), 2 = 16-bit integers (0–65535). Omitted for floating-point." }, + "table": { "type": "array", "items": { "type": "number" }, "description": "Curve samples (when curveType is 'table'). Integers when precision is present, floats otherwise." } + } + }, + + "ParametricCurveTypeContent": { + "type": "object", + "description": "parametricCurveType", + "required": ["functionType", "params"], + "properties": { + "functionType": { "type": "integer", "minimum": 0, "maximum": 4, "description": "ICC parametric curve function type (0–4)" }, + "params": { "type": "array", "items": { "type": "number" }, "description": "Function parameters (count depends on functionType: 0→1, 1→3, 2→4, 3→5, 4→7)" } + } + }, + + "SegmentedCurveObject": { + "type": "object", + "description": "A segmented curve containing one or more segments covering the real line", + "required": ["segments"], + "properties": { + "segments": { + "type": "array", + "items": { "$ref": "#/$defs/CurveSegment" }, + "minItems": 1 + } + } + }, + + "CurveSegment": { + "type": "object", + "description": "One segment of a segmented curve. start/end may be a number or the strings '+infinity'/'-infinity'.", + "required": ["start", "end", "type"], + "properties": { + "start": { "oneOf": [{ "type": "number" }, { "type": "string", "enum": ["-infinity", "+infinity"] }] }, + "end": { "oneOf": [{ "type": "number" }, { "type": "string", "enum": ["-infinity", "+infinity"] }] }, + "type": { "type": "string", "enum": ["FormulaSegment", "SampledSegment"] }, + "functionType": { "type": "integer", "description": "Formula function type (0–7); FormulaSegment only" }, + "parameters": { "type": "array", "items": { "type": "number" }, "description": "Formula parameters; FormulaSegment only" }, + "reserved": { "type": "integer", "minimum": 0, "description": "Segment reserved word (FormulaSegment only, omitted when zero)" }, + "reserved2": { "type": "integer", "minimum": 0, "description": "Segment reserved2 field (FormulaSegment only, omitted when zero)" }, + "samples": { "type": "array", "items": { "type": "number" }, "description": "Sample values; SampledSegment only" } + } + }, + + "XYZArrayTypeContent": { + "type": "object", + "description": "XYZArrayType — one or more XYZ tristimulus values", + "required": ["XYZ"], + "properties": { + "XYZ": { "type": "array", "items": { "$ref": "#/$defs/XYZTriplet" } } + } + }, + + "NumericArrayTypeContent": { + "type": "object", + "description": "Numeric array types (uInt8ArrayType, uInt16ArrayType, uInt32ArrayType, uInt64ArrayType, float16/32/64ArrayType, s15Fixed16ArrayType, u16Fixed16ArrayType, sparseMatrixArrayType)", + "required": ["values"], + "properties": { + "values": { "type": "array", "items": { "type": "number" } } + } + }, + + "ChromaticityTypeContent": { + "type": "object", + "description": "chromaticityType", + "required": ["colorantType", "channels"], + "properties": { + "colorantType": { "type": "integer" }, + "channels": { + "type": "array", + "items": { + "type": "object", + "required": ["x", "y"], + "properties": { + "x": { "type": "number" }, + "y": { "type": "number" } + } + } + } + } + }, + + "CicpTypeContent": { + "type": "object", + "description": "cicpType (ITU-T H.273 coding-independent code points)", + "required": ["colourPrimaries", "transferCharacteristics", "matrixCoefficients", "videoFullRangeFlag"], + "properties": { + "colourPrimaries": { "type": "integer" }, + "transferCharacteristics": { "type": "integer" }, + "matrixCoefficients": { "type": "integer" }, + "videoFullRangeFlag": { "type": "integer", "minimum": 0, "maximum": 1 } + } + }, + + "ColorantOrderTypeContent": { + "type": "object", + "description": "colorantOrderType", + "required": ["colorantOrder"], + "properties": { + "colorantOrder": { "type": "array", "items": { "type": "integer", "minimum": 0 } } + } + }, + + "ColorantTableTypeContent": { + "type": "object", + "description": "colorantTableType", + "required": ["colorantTable"], + "properties": { + "colorantTable": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "pcs"], + "properties": { + "name": { "type": "string" }, + "pcs": { "$ref": "#/$defs/XYZTriplet" } + } + } + } + } + }, + + "DataTypeContent": { + "type": "object", + "description": "dataType — raw binary or ASCII data", + "required": ["dataFlag", "data"], + "properties": { + "dataFlag": { "type": "integer", "description": "0=binary, 1=ASCII" }, + "data": { "$ref": "#/$defs/HexString" } + } + }, + + "DateTimeTypeContent": { + "type": "object", + "description": "dateTimeType", + "required": ["dateTime"], + "properties": { + "dateTime": { "$ref": "#/$defs/DateTime" } + } + }, + + "MeasurementTypeContent": { + "type": "object", + "description": "measurementType", + "required": ["standardObserver", "backing", "geometry", "flare", "illuminant"], + "properties": { + "standardObserver": { "type": "integer" }, + "backing": { "$ref": "#/$defs/XYZTriplet" }, + "geometry": { "type": "integer" }, + "flare": { "type": "number" }, + "illuminant": { "type": "integer" } + } + }, + + "ViewingConditionsTypeContent": { + "type": "object", + "description": "viewingConditionsType", + "required": ["illuminant", "surround", "illuminantType"], + "properties": { + "illuminant": { "$ref": "#/$defs/XYZTriplet" }, + "surround": { "$ref": "#/$defs/XYZTriplet" }, + "illuminantType": { "type": "integer" } + } + }, + + "SpectralDataInfoTypeContent": { + "type": "object", + "description": "spectralDataInfoType", + "required": ["spectralColorSig", "spectralRange"], + "properties": { + "spectralColorSig": { "$ref": "#/$defs/Signature" }, + "spectralRange": { "$ref": "#/$defs/SpectralRange" }, + "biSpectralRange": { "$ref": "#/$defs/SpectralRange" } + } + }, + + "SpectralDataArray": { + "type": "object", + "description": "Spectral function data with wavelength range and sample values", + "required": ["start", "end", "steps", "data"], + "properties": { + "start": { "type": "number" }, + "end": { "type": "number" }, + "steps": { "type": "integer", "minimum": 1 }, + "Reserved": { "type": "integer", "minimum": 0 }, + "data": { "type": "array", "items": { "type": "number" } } + } + }, + + "SpectralViewingConditionsTypeContent": { + "type": "object", + "description": "spectralViewingConditionsType", + "required": ["StdObserver", "IlluminantXYZ", "StdIlluminant", "ColorTemperature", "SurroundXYZ"], + "properties": { + "StdObserver": { "$ref": "#/$defs/Signature" }, + "IlluminantXYZ": { "$ref": "#/$defs/XYZTriplet" }, + "ObserverFuncs": { "type": "array", "items": { "$ref": "#/$defs/SpectralDataArray" } }, + "StdIlluminant": { "$ref": "#/$defs/Signature" }, + "ColorTemperature": { "type": "number" }, + "IlluminantSPD": { "type": "array", "items": { "$ref": "#/$defs/SpectralDataArray" } }, + "SurroundXYZ": { "$ref": "#/$defs/XYZTriplet" } + } + }, + + "LocalizedString": { + "type": "object", + "description": "Language/country code pair with localized text", + "required": ["language", "country", "text"], + "properties": { + "language": { "type": "string", "minLength": 2, "maxLength": 2 }, + "country": { "type": "string", "minLength": 2, "maxLength": 2 }, + "text": { "type": "string" } + } + }, + + "MultiLocalizedUnicodeTypeContent": { + "type": "object", + "description": "multiLocalizedUnicodeType", + "required": ["localizedStrings"], + "properties": { + "localizedStrings": { + "type": "array", + "items": { "$ref": "#/$defs/LocalizedString" } + } + } + }, + + "NamedColor2TypeContent": { + "type": "object", + "description": "namedColor2Type", + "required": ["countOfDeviceCoords", "colorantPrefix", "colorantSuffix", "colors"], + "properties": { + "vendorFlag": { "type": "integer" }, + "countOfDeviceCoords": { "type": "integer", "minimum": 0 }, + "colorantPrefix": { "type": "string" }, + "colorantSuffix": { "type": "string" }, + "colors": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "pcsCoords"], + "properties": { + "name": { "type": "string" }, + "pcsCoords": { "$ref": "#/$defs/XYZTriplet" }, + "deviceCoords": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 65535 }, "description": "Device colour coordinates as 16-bit integers (0–65535)" } + } + } + } + } + }, + + "ProfileSequenceDescTypeContent": { + "type": "object", + "description": "profileSequenceDescType", + "required": ["profileSequence"], + "properties": { + "profileSequence": { + "type": "array", + "items": { "$ref": "#/$defs/ProfileDescEntry" } + } + } + }, + + "ProfileDescEntry": { + "type": "object", + "description": "Per-profile description entry within a profileSequenceDescType", + "properties": { + "deviceManufacturerSignature": { "$ref": "#/$defs/Signature" }, + "deviceModelSignature": { "$ref": "#/$defs/Signature" }, + "deviceAttributes": { "type": "object" }, + "technology": { "$ref": "#/$defs/Signature" }, + "deviceManufacturer": { "$ref": "#/$defs/TagTypeData", "description": "Flat tag with 'type' field (textDescriptionType, utf8TextType, etc.)" }, + "deviceModel": { "$ref": "#/$defs/TagTypeData", "description": "Flat tag with 'type' field (textDescriptionType, utf8TextType, etc.)" } + } + }, + + "ProfileSequenceIdentifierTypeContent": { + "type": "object", + "description": "profileSequenceIdentifierType", + "required": ["profileSequenceId"], + "properties": { + "profileSequenceId": { + "type": "array", + "items": { + "type": "object", + "description": "Per-profile ID and description entry", + "properties": { + "profileId": { "type": "string", "description": "32-character uppercase hex string (16-byte profile ID)" }, + "localizedStrings": { "type": "array", "items": { "$ref": "#/$defs/LocalizedString" } } + } + } + } + } + }, + + "ResponseCurveSet16TypeContent": { + "type": "object", + "description": "responseCurveSet16Type", + "required": ["CountOfChannels", "ResponseCurves"], + "properties": { + "CountOfChannels": { "type": "integer", "minimum": 1 }, + "ResponseCurves": { + "type": "array", + "items": { + "type": "object", + "properties": { + "MeasurementUnit": { "type": "string" }, + "XYZ": { "$ref": "#/$defs/XYZTriplet" }, + "MeasValues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "DeviceCode": { "type": "integer" }, + "MeasValue": { "type": "number" }, + "Reserved": { "type": "integer" } + } + } + } + } + } + } + } + }, + + "MBBCurve": { + "type": "object", + "description": "A single curve in an MBB LUT tag curve array. Discriminated by 'type'.", + "required": ["type"], + "properties": { + "type": { "type": "string", "enum": ["Curve", "ParametricCurve", "SegmentedCurve"] }, + "curveType": { "type": "string", "enum": ["identity", "gamma", "table"] }, + "gamma": { "type": "number" }, + "precision": { "type": "integer", "enum": [1, 2], "description": "Table encoding: 1 = 8-bit (0–255), 2 = 16-bit (0–65535). Omitted for float curves." }, + "table": { "type": "array", "items": { "type": "number" }, "description": "Integers when precision present, floats otherwise." }, + "functionType": { "type": "integer" }, + "params": { "type": "array", "items": { "type": "number" } }, + "segmentedCurve": { "$ref": "#/$defs/SegmentedCurveObject" } + } + }, + + "CLUTObject": { + "type": "object", + "description": "Color lookup table data. Data values are integers when precision is 1 (0–255) or 2 (0–65535), or floating-point numbers for float CLUTs (no precision field).", + "required": ["data"], + "properties": { + "gridPoints": { + "type": "array", + "items": { "type": "integer", "minimum": 2 }, + "description": "Grid point count per input dimension (one entry per input channel)" + }, + "precision": { + "type": "integer", + "enum": [1, 2], + "description": "Storage precision: 1 = 8-bit (values 0–255), 2 = 16-bit (values 0–65535). Omitted for floating-point CLUTs." + }, + "data": { + "type": "array", + "items": { "type": "number" }, + "description": "Flattened output values in row-major order. Integers when precision is present, floats otherwise." + } + } + }, + + "MatrixObject": { + "type": "object", + "description": "3×3 or 3×4 matrix (with optional translation constants)", + "required": ["e"], + "properties": { + "e": { + "type": "array", + "items": { "type": "number" }, + "minItems": 9, + "maxItems": 12, + "description": "Matrix coefficients in row-major order. 9 values = no constants; 12 values = with translation constants." + } + } + }, + + "LutMBBTypeContent": { + "type": "object", + "description": "lut8Type / lut16Type / lutAtoBType / lutBtoAType — MBB LUT structure", + "required": ["inputChannels", "outputChannels"], + "properties": { + "inputChannels": { "type": "integer", "minimum": 1, "maximum": 15 }, + "outputChannels": { "type": "integer", "minimum": 1, "maximum": 15 }, + "bCurves": { "type": "array", "items": { "$ref": "#/$defs/MBBCurve" }, "description": "B-side curves (input or output depending on tag type)" }, + "mCurves": { "type": "array", "items": { "$ref": "#/$defs/MBBCurve" }, "description": "M-side curves (between matrix and CLUT)" }, + "aCurves": { "type": "array", "items": { "$ref": "#/$defs/MBBCurve" }, "description": "A-side curves (the other side from B)" }, + "matrix": { "$ref": "#/$defs/MatrixObject" }, + "clut": { "$ref": "#/$defs/CLUTObject" } + } + }, + + "MultiProcessElementTypeContent": { + "type": "object", + "description": "multiProcessElementType — a sequence of MPE processing elements", + "required": ["inputChannels", "outputChannels", "elements"], + "properties": { + "inputChannels": { "type": "integer", "minimum": 1 }, + "outputChannels": { "type": "integer", "minimum": 1 }, + "elements": { + "type": "array", + "description": "Ordered list of MPE elements. Each item is a flat object with a 'type' discriminator field.", + "items": { "$ref": "#/$defs/MPEElementEntry" } + } + } + }, + + "MPEElementEntry": { + "type": "object", + "description": "Flat MPE element object with 'type' discriminator field. Always contains inputChannels, outputChannels, and type-specific properties at the top level.", + "required": ["type", "inputChannels", "outputChannels"], + "properties": { + "type": { "type": "string" }, + "inputChannels": { "type": "integer", "minimum": 1 }, + "outputChannels": { "type": "integer", "minimum": 1 }, + "Reserved": { "type": "integer", "minimum": 0, "description": "Element-level reserved word (omitted when zero)" } + }, + "oneOf": [ + { "allOf": [{ "properties": { "type": { "const": "CurveSetElement" } } }, { "$ref": "#/$defs/CurveSetElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "MatrixElement" } } }, { "$ref": "#/$defs/MatrixElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "CLutElement" } } }, { "$ref": "#/$defs/CLutElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "ExtCLutElement" } } }, { "$ref": "#/$defs/ExtCLutElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "BAcsElement" } } }, { "$ref": "#/$defs/AcsElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "EAcsElement" } } }, { "$ref": "#/$defs/AcsElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "CalculatorElement" } } }, { "$ref": "#/$defs/CalculatorElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "XYZToJabElement" } } }, { "$ref": "#/$defs/JabElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "JabToXYZElement" } } }, { "$ref": "#/$defs/JabElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "TintArrayElement" } } }, { "$ref": "#/$defs/TintArrayElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "ToneMapElement" } } }, { "$ref": "#/$defs/ToneMapElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "EmissionMatrixElement" } } }, { "$ref": "#/$defs/SpectralMatrixElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "InvEmissionMatrixElement" } } }, { "$ref": "#/$defs/SpectralMatrixElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "EmissionCLutElement" } } }, { "$ref": "#/$defs/SpectralCLutElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "ReflectanceCLutElement" } } }, { "$ref": "#/$defs/SpectralCLutElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "EmissionObserverElement" } } }, { "$ref": "#/$defs/SpectralObserverElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "ReflectanceObserverElement" } } }, { "$ref": "#/$defs/SpectralObserverElementContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "SparseMatrixElement" } } }, { "$ref": "#/$defs/MPEBaseContent" }] }, + { "allOf": [{ "properties": { "type": { "const": "UnknownElement" } } }, { "$ref": "#/$defs/MPEBaseContent" }] } + ] + }, + + "MPEBaseContent": { + "type": "object", + "description": "Common fields present in every MPE element content object", + "properties": { + "inputChannels": { "type": "integer", "minimum": 1 }, + "outputChannels": { "type": "integer", "minimum": 1 }, + "Reserved": { "type": "integer", "minimum": 0, "description": "Element-level reserved word (omitted when zero)" } + } + }, + + "CurveSetElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "CurveSetElement — one independent processing curve per channel", + "required": ["inputChannels", "outputChannels", "curves"], + "properties": { + "curves": { + "type": "array", + "description": "One curve entry per channel", + "items": { "$ref": "#/$defs/CurveSetCurve" } + } + } + }, + + "CurveSetCurve": { + "type": "object", + "description": "A single curve within a CurveSetElement. Discriminated by 'type'.", + "required": ["type"], + "properties": { + "type": { "type": "string", "enum": ["SegmentedCurve", "SingleSampledCurve", "SampledCalculatorCurve", "DuplicateCurve"] }, + "index": { "type": "integer", "minimum": 0, "description": "Source curve index (DuplicateCurve only)" }, + "segments": { "type": "array", "items": { "$ref": "#/$defs/CurveSegment" }, "description": "Segment list (SegmentedCurve only)" }, + "firstEntry": { "type": "number" }, + "lastEntry": { "type": "number" }, + "storageType": { "type": "integer", "description": "Storage type code (SingleSampledCurve only)" }, + "extensionType": { "type": "integer", "description": "Extension behaviour beyond range (0=clip, 1=linear)" }, + "reserved": { "type": "integer", "minimum": 0, "description": "Reserved field (omitted when zero)" }, + "samples": { "type": "array", "items": { "type": "number" }, "description": "Sampled values (SingleSampledCurve only)" }, + "desiredSize": { "type": "integer", "minimum": 0, "description": "Desired number of sample points (SampledCalculatorCurve only)" }, + "reserved2": { "type": "integer", "minimum": 0, "description": "Reserved2 field (SampledCalculatorCurve only, omitted when zero)" }, + "calculator": { "$ref": "#/$defs/CalculatorElementContent", "description": "Embedded calculator (SampledCalculatorCurve only)" } + } + }, + + "MatrixElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "MatrixElement — linear transform with optional constants", + "required": ["inputChannels", "outputChannels"], + "properties": { + "matrix": { "type": "array", "items": { "type": "number" }, "description": "Flattened matrix coefficients" }, + "constants": { "type": "array", "items": { "type": "number" }, "description": "Additive constants (one per output channel)" }, + "invertMatrix": { "type": "boolean" } + } + }, + + "CLutElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "CLutElement — N-D color lookup table", + "required": ["inputChannels", "outputChannels"], + "properties": { + "clut": { "$ref": "#/$defs/CLUTObject" } + } + }, + + "ExtCLutElementContent": { + "allOf": [{ "$ref": "#/$defs/CLutElementContent" }], + "description": "ExtCLutElement — extended CLUT with explicit storage type", + "properties": { + "storageType": { "type": "integer" }, + "reserved2": { "type": "integer", "minimum": 0 } + } + }, + + "AcsElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "BAcsElement / EAcsElement — auxiliary color space conversion bookend", + "required": ["inputChannels", "outputChannels"], + "properties": { + "signature": { "$ref": "#/$defs/Signature" }, + "data": { "$ref": "#/$defs/HexString" } + } + }, + + "CalculatorElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "CalculatorElement — programmable calculator with named channels, variables, macros, and sub-elements", + "required": ["inputChannels", "outputChannels"], + "properties": { + "inputNames": { "type": "string", "description": "Named input channel specification (space-separated names)" }, + "outputNames": { "type": "string", "description": "Named output channel specification (space-separated names)" }, + "imports": { + "type": "array", + "description": "Files to import variable/macro/element definitions from", + "items": { + "type": "object", + "required": ["file"], + "properties": { + "file": { "type": "string" } + } + } + }, + "variables": { + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "position": { "type": "integer", "minimum": 0, "description": "Fixed allocation offset (optional)" }, + "size": { "type": "integer", "minimum": 1 }, + "members": { "type": "string", "description": "Member name specification string" } + } + } + }, + "macros": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "body"], + "properties": { + "name": { "type": "string" }, + "body": { "type": "string", "description": "Macro function token string" }, + "locals": { "type": "string", "description": "Local variable specification" } + } + } + }, + "subElements": { + "type": "array", + "description": "Named embedded MPE elements referenced from mainFunction", + "items": { + "type": "object", + "description": "Flat object with 'name' (element name), 'type' (MPE element type), plus type-specific fields", + "required": ["name", "type"], + "properties": { + "name": { "type": "string", "description": "Element name used in mainFunction references" } + }, + "allOf": [{ "$ref": "#/$defs/MPEElementEntry" }] + } + }, + "mainFunction": { "type": "string", "description": "Compiled calculator function token string" } + } + }, + + "JabElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "XYZToJabElement / JabToXYZElement — CIECAM02 color appearance model conversion", + "required": ["inputChannels", "outputChannels"], + "properties": { + "colorAppearanceParams": { + "type": "object", + "properties": { + "whitePoint": { "$ref": "#/$defs/XYZTriplet" }, + "surroundFraction": { "type": "number" }, + "adaptationFactor": { "type": "number" } + } + } + } + }, + + "TintArrayElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "TintArrayElement — spectral tinting via a tag array", + "required": ["inputChannels", "outputChannels"], + "properties": { + "arrayType": { "type": "string", "description": "Tag type name of the embedded array" }, + "values": { "type": "array", "items": { "type": "number" } }, + "file": { "type": "string", "description": "External file path (alternative to inline values)" }, + "format": { "type": "string", "enum": ["text", "binary"] } + } + }, + + "ToneMapElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "ToneMapElement", + "required": ["inputChannels", "outputChannels"], + "properties": { + "luminanceCurve": { "$ref": "#/$defs/SegmentedCurveObject" }, + "toneMapFunctions": { "type": "array", "items": { "type": "object" } } + } + }, + + "SpectralMatrixElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "EmissionMatrixElement / InvEmissionMatrixElement — spectral-to-colorimetric matrix", + "required": ["inputChannels", "outputChannels", "wavelengths"], + "properties": { + "wavelengths": { "$ref": "#/$defs/SpectralRange" }, + "whiteData": { "type": "array", "items": { "type": "number" }, "description": "White spectral data (one value per wavelength step)" }, + "matrixData": { "type": "array", "items": { "type": "number" }, "description": "Flattened matrix (outputChannels × steps)" }, + "offsetData": { "type": "array", "items": { "type": "number" }, "description": "Matrix offset vector (one value per output channel)" } + } + }, + + "SpectralCLutElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "EmissionCLutElement / ReflectanceCLutElement — spectral CLUT", + "required": ["inputChannels", "outputChannels", "flags", "storageType", "wavelengths"], + "properties": { + "flags": { "type": "integer" }, + "storageType": { "type": "integer" }, + "wavelengths": { "$ref": "#/$defs/SpectralRange" }, + "whiteData": { "type": "array", "items": { "type": "number" } }, + "clut": { "$ref": "#/$defs/CLUTObject" } + } + }, + + "SpectralObserverElementContent": { + "allOf": [{ "$ref": "#/$defs/MPEBaseContent" }], + "description": "EmissionObserverElement / ReflectanceObserverElement — observer spectral weighting", + "required": ["inputChannels", "outputChannels", "flags", "wavelengths"], + "properties": { + "flags": { "type": "integer" }, + "wavelengths":{ "$ref": "#/$defs/SpectralRange" }, + "whiteData": { "type": "array", "items": { "type": "number" } } + } + }, + + "DictTypeContent": { + "type": "object", + "description": "dictType — name/value pairs with optional localization", + "required": ["entries"], + "properties": { + "entries": { + "type": "array", + "items": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string" }, + "value": { "type": "string" }, + "localizedNames": { "type": "array", "items": { "$ref": "#/$defs/LocalizedString" } }, + "localizedValues":{ "type": "array", "items": { "$ref": "#/$defs/LocalizedString" } } + } + } + } + } + }, + + "TagStructTypeContent": { + "type": "object", + "description": "tagStructType — named structure containing member tags", + "required": ["structureType"], + "properties": { + "structureType": { "type": "string", "description": "Named structure type or 'privateStruct'" }, + "structureSignature": { "$ref": "#/$defs/Signature", "description": "Signature when structureType is 'privateStruct'" }, + "memberTags": { + "type": "array", + "description": "Each item is a single-key object { elemName: { type, ...fields } }", + "items": { + "type": "object", + "description": "Single-key object mapping element name to its flat tag content", + "minProperties": 1, + "maxProperties": 1, + "additionalProperties": { "$ref": "#/$defs/StructMemberTagContent" } + } + } + } + }, + + "StructMemberTagContent": { + "type": "object", + "description": "Content of a struct member tag. Either a 'sameAs' alias, or a flat tag with 'type' field.", + "properties": { + "sig": { "$ref": "#/$defs/Signature", "description": "Element signature (private sub-tags only)" }, + "sameAs": { "type": "string", "description": "Name of element that owns the shared tag (aliased members)" }, + "typeSig": { "$ref": "#/$defs/Signature", "description": "Raw type signature (PrivateType only)" }, + "Reserved": { "type": "integer", "minimum": 0 } + }, + "oneOf": [ + { "required": ["sameAs"] }, + { "required": ["type"], "properties": { "type": { "type": "string" } } } + ] + }, + + "TagArrayTypeContent": { + "type": "object", + "description": "tagArrayType — typed array of tags", + "required": ["arrayType"], + "properties": { + "arrayType": { "type": "string", "description": "Named array type or 'privateArray'" }, + "arraySignature": { "$ref": "#/$defs/Signature", "description": "Signature when arrayType is 'privateArray'" }, + "arrayTags": { + "type": "array", + "description": "Each item is a flat object with 'type' discriminator plus type-specific fields", + "items": { "$ref": "#/$defs/TagTypeData" } + } + } + }, + + "EmbeddedHeightImageTypeContent": { + "type": "object", + "description": "embeddedHeightImageType", + "required": ["SeamlessIndicator", "EncodingFormat", "MetersMinPixelValue", "MetersMaxPixelValue"], + "properties": { + "SeamlessIndicator": { "type": "integer" }, + "EncodingFormat": { "type": "integer" }, + "MetersMinPixelValue": { "type": "number" }, + "MetersMaxPixelValue": { "type": "number" }, + "ImageData": { "$ref": "#/$defs/HexString" } + } + }, + + "EmbeddedNormalImageTypeContent": { + "type": "object", + "description": "embeddedNormalImageType", + "required": ["SeamlessIndicator", "EncodingFormat"], + "properties": { + "SeamlessIndicator": { "type": "integer" }, + "EncodingFormat": { "type": "integer" }, + "ImageData": { "$ref": "#/$defs/HexString" } + } + }, + + "EmbeddedProfileTypeContent": { + "type": "object", + "description": "embeddedProfileType — contains a nested ICC profile", + "required": ["IccProfile"], + "properties": { + "IccProfile": { "$ref": "#/$defs/Profile" } + } + }, + + "GamutBoundaryDescTypeContent": { + "type": "object", + "description": "gamutBoundaryDescType — mesh of PCS (and optional device) vertices with triangle connectivity", + "required": ["Vertices", "Triangles"], + "properties": { + "Vertices": { + "type": "object", + "required": ["PCSChannels", "PCSValues"], + "properties": { + "PCSChannels": { "type": "integer" }, + "PCSValues": { "type": "array", "items": { "type": "number" } }, + "DeviceChannels": { "type": "integer" }, + "DeviceValues": { "type": "array", "items": { "type": "number" } } + } + }, + "Triangles": { + "type": "array", + "items": { + "type": "array", + "items": { "type": "integer", "minimum": 0 }, + "minItems": 3, + "maxItems": 3 + } + } + } + }, + + "PrivateTypeContent": { + "type": "object", + "description": "PrivateType — unknown tag type stored as raw hex", + "properties": { + "sig": { "$ref": "#/$defs/Signature" }, + "data": { "$ref": "#/$defs/HexString" } + } + } + + } +} diff --git a/docs/iccjson.md b/docs/iccjson.md new file mode 100644 index 000000000..b37a1442b --- /dev/null +++ b/docs/iccjson.md @@ -0,0 +1,802 @@ +# Working with ICC Profiles Using IccJSON + +IccJSON provides two command-line tools — **IccToJson** and **IccFromJson** — that let you +convert ICC profiles to and from a human-readable JSON representation. This makes it +straightforward to inspect, create, and edit ICC and iccMAX profiles using a text editor +or any JSON-capable tooling. + +A JSON Schema for the format is provided at [`docs/icc-profile.schema.json`](icc-profile.schema.json). +Editors such as VS Code can use it to provide inline validation and auto-complete. + +--- + +## Tools + +### IccToJson + +Converts a binary ICC profile to a JSON file. + +``` +IccToJson src_profile.icc dest_profile.json [-indent=N] +``` + +- `-indent=N` — pretty-print with N spaces of indentation (default: 2) + +### IccFromJson + +Converts a JSON file back to a binary ICC profile. + +``` +IccFromJson src_profile.json dest_profile.icc [-noid] +``` + +- `-noid` — suppress writing the profile ID (MD5 hash) into the saved file + +If the profile fails validation the binary file is still saved, but the validation +report is printed so you can identify and fix issues before use. + +### Validating a Profile + +After creating a profile with IccFromJson, use **IccDumpProfile** to validate it: + +``` +IccDumpProfile dest_profile.icc +``` + +The overall pass/fail result appears two lines below the `Validation Report` line: + +```bash +grep --text -A 3 "^Validation Report" output.txt +``` + +--- + +## Typical Workflow + +``` +# Inspect an existing profile +IccToJson input.icc input.json + +# Edit input.json in a text editor … + +# Rebuild the binary profile +IccFromJson input.json output.icc + +# Validate +IccDumpProfile output.icc +``` + +--- + +## JSON Structure + +Every ICC profile JSON file is wrapped in a top-level `"IccProfile"` key: + +```json +{ + "IccProfile": { + "Header": { ... }, + "Tags": [ ... ] + } +} +``` + +### Header + +The `Header` object describes the profile class, colour spaces, and metadata. +Signature fields that are zero (unset) are encoded as an empty string `""`. Optional non-signature fields (e.g. `ProfileSubClass`, `SpectralPCS`) are omitted when not set. + +```json +"Header": { + "ProfileVersion": "5.0", + "ProfileDeviceClass": "mntr", + "DataColourSpace": "RGB ", + "PCS": "XYZ ", + "CreationDateTime": "2024-01-15T10:30:00", + "ProfileFileSignature": "acsp", + "PrimaryPlatform": "MSFT", + "CMMFlags": { + "EmbeddedInFile": false, + "UseWithEmbeddedDataOnly": false + }, + "DeviceAttributes": { + "ReflectiveOrTransparency": "reflective", + "GlossyOrMatte": "glossy", + "MediaPolarity": "positive", + "MediaColour": "colour" + }, + "RenderingIntent": "Relative", + "PCSIlluminant": [0.9642, 1.0, 0.8249], + "ProfileCreator": "ICC " +} +``` + +Common `ProfileDeviceClass` values: + +| Value | Meaning | +|--------|-------------------| +| `scnr` | Scanner | +| `mntr` | Display / Monitor | +| `prtr` | Printer / Output | +| `link` | DeviceLink | +| `spac` | Color Space | +| `abst` | Abstract | +| `nmcl` | Named Color | +| `cenc` | Color Encoding | + +Common `DataColourSpace` / `PCS` values: `RGB `, `CMYK`, `XYZ `, `Lab `. + +For iccMAX spectral profiles, add: + +```json +"SpectralPCS": "rs0024", +"SpectralRange": { "start": 380.0, "end": 730.0, "steps": 36 } +... + +For iccMAX bisspectral profiles, add: + +.. +"BiSpectralRange": { "start": 300.0, "end": 730.0, "steps": 44 } +``` + +### Tags Array + +`Tags` is an ordered JSON array. Each element is a **single-key object** whose key is +the ICC tag name (e.g. `"redMatrixColumnTag"`) and whose value holds the tag data. + +```json +"Tags": [ + { + "redMatrixColumnTag": { + "data": { + "type": "XYZType", + "XYZ": [0.4361, 0.2225, 0.0139] + } + } + } +] +``` + +The `data` object always contains a `"type"` field identifying the ICC tag type, +followed by the type-specific fields at the same level. + +**Shared tags** (where two tag signatures point to the same data) are written with a +`"sameAs"` field referencing the key of the first occurrence: + +```json +{ "AToB1Tag": { "sameAs": "AToB0Tag" } } +``` + +**Private tags** use a generated key (`"PrivateTag_1"`, etc.) and carry a `"sig"` field +with the raw four-character signature: + +```json +{ "PrivateTag_1": { "sig": "cust", "data": { "type": "PrivateType", ... } } } +``` + +--- + +## Common Tag Types + +### Text Tags + +#### `multiLocalizedUnicodeType` — localised Unicode text + +Used for `profileDescriptionTag` (desc), `copyrightTag`, and similar tags. + +```json +{ + "profileDescriptionTag": { + "data": { + "type": "multiLocalizedUnicodeType", + "localizedStrings": [ + { "language": "en", "country": "US", "text": "My sRGB Profile" } + ] + } + } +} +``` + +#### `utf8TextType` — simple UTF-8 string + +```json +{ + "charTargetTag": { + "data": { + "type": "utf8TextType", + "text": "My target name" + } + } +} +``` + +#### `textType` — ASCII text (ICC v2/v4) + +```json +{ + "copyrightTag": { + "data": { + "type": "textType", + "text": "Copyright 2024 My Company" + } + } +} +``` + +### Numeric Tags + +#### `XYZType` — one or more XYZ tristimulus values + +Each XYZ value is a JSON array `[X, Y, Z]`. The `"XYZ"` field is an array of such triplets. + +```json +{ + "mediaWhitePointTag": { + "data": { + "type": "XYZType", + "XYZ": [0.9505, 1.0000, 1.0891] + } + } +} +``` + +#### `s15Fixed16ArrayType` / `u16Fixed16ArrayType` — numeric arrays + +```json +{ + "chromaticAdaptationTag": { + "data": { + "type": "s15Fixed16ArrayType", + "values": [ 1.0479, 0.0229, -0.0502, + 0.0296, 0.9904, -0.0171, + -0.0092, 0.0151, 0.7519 ] + } + } +} +``` + +### Curve Tags + +#### `curveType` — tone-reproduction curve (TRC) + +The `"curveType"` field selects one of three subtypes: `"identity"`, `"gamma"`, or `"table"`. + +Identity (no-op): +```json +{ + "redTRCTag": { + "data": { "type": "curveType", "curveType": "identity" } + } +} +``` + +Gamma: +```json +{ + "redTRCTag": { + "data": { "type": "curveType", "curveType": "gamma", "gamma": 2.2 } + } +} +``` + +Sampled table: +```json +{ + "redTRCTag": { + "data": { + "type": "curveType", + "curveType": "table", + "table": [0.0, 0.0031, 0.0145, 0.0331, ...] + } + } +} +``` + +#### `parametricCurveType` — parametric TRC + +```json +{ + "redTRCTag": { + "data": { + "type": "parametricCurveType", + "functionType": 3, + "params": [2.4, 0.9479, 0.0521, 0.0405, 0.0031] + } + } +} +``` + +`functionType` parameter counts: + +| `functionType` | Formula | `params` count | +|----------------|--------------------------------|----------------| +| `0` | `Y = X^γ` | 1 | +| `1` | `Y = (aX + b)^γ` | 3 | +| `2` | `Y = (aX + b)^γ + c` | 4 | +| `3` | sRGB-style (two-segment) | 5 | +| `4` | Full CIE 122-1996 (seven-param)| 7 | + +#### `segmentedCurveType` — piecewise segmented curve (iccMAX) + +Segments cover the real line with `"-infinity"` / `"+infinity"` at the outermost boundaries. +Segment types are `"FormulaSegment"` or `"SampledSegment"`. + +```json +{ + "redTRCTag": { + "data": { + "type": "segmentedCurveType", + "segments": [ + { + "start": "-infinity", + "end": 0.04045, + "type": "FormulaSegment", + "functionType": 0, + "parameters": [1.0, 0.07739938, 0.0125, 0.0] + }, + { + "start": 0.04045, + "end": "+infinity", + "type": "FormulaSegment", + "functionType": 0, + "parameters": [2.4, 0.94786562, 0.05213438, 0.0031308] + } + ] + } + } +} +``` + +Segment types: + +| `type` | Required fields | +|-------------------|-------------------------------| +| `FormulaSegment` | `functionType`, `parameters` | +| `SampledSegment` | `samples` | + +### Signature Tags + +#### `signatureType` + +```json +{ + "technologyTag": { + "data": { + "type": "signatureType", + "signature": "CRT " + } + } +} +``` + +### LUT Tags + +#### `lutAtoBType` / `lutBtoAType` — multi-stage LUT + +MBB (Multidimensional Black Box) LUT curve arrays (`bCurves`, `mCurves`, `aCurves`) use curve objects with +`"type"` set to `"Curve"`, `"ParametricCurve"`, or `"SegmentedCurve"`. + +Both CLUT data and sampled curve tables (`"curveType": "table"`) use a `"precision"` field +to indicate integer encoding, matching the behaviour of iccXML: + +| `"precision"` | Data value range | Storage | +|---------------|-----------------|---------| +| `1` | 0 – 255 | 8-bit | +| `2` | 0 – 65535 | 16-bit | +| *(absent)* | floating-point | float | + +Named colour device coordinates (`"deviceCoords"`) are always 16-bit integers (0–65535). + +```json +{ + "AToB0Tag": { + "data": { + "type": "lutAtoBType", + "inputChannels": 3, + "outputChannels": 3, + "bCurves": [ + { "type": "Curve", "curveType": "identity" }, + { "type": "Curve", "curveType": "identity" }, + { "type": "Curve", "curveType": "identity" } + ], + "matrix": { + "e": [ 0.4361, 0.3851, 0.1431, + 0.2225, 0.7169, 0.0606, + 0.0139, 0.0971, 0.7141 ] + }, + "mCurves": [ + { "type": "Curve", "curveType": "identity" }, + { "type": "Curve", "curveType": "identity" }, + { "type": "Curve", "curveType": "identity" } + ], + "clut": { + "gridPoints": [9, 9, 9], + "precision": 2, + "data": [0, 0, 0, 128, 64, 32, ...] + }, + "aCurves": [ + { "type": "Curve", "curveType": "identity" }, + { "type": "Curve", "curveType": "identity" }, + { "type": "Curve", "curveType": "identity" } + ] + } + } +} +``` + +MBB curve type values: + +| `type` | Subtype fields | +|------------------|---------------------------------------------| +| `Curve` | `curveType` (`"identity"`, `"gamma"`, `"table"`), `gamma`, `table` | +| `ParametricCurve`| `functionType`, `params` | +| `SegmentedCurve` | `segments` array | + +--- + +## Multi-Process Element (MPE) Tags + +iccMAX profiles use `multiProcessElementType` tags containing an ordered pipeline +of processing elements. Each element is a **flat object** with a `"type"` discriminator +and `"inputChannels"` / `"outputChannels"` counts. + +```json +{ + "AToB1Tag": { + "data": { + "type": "multiProcessElementType", + "inputChannels": 3, + "outputChannels": 3, + "elements": [ + { + "type": "CurveSetElement", + "inputChannels": 3, + "outputChannels": 3, + "curves": [ ... ] + }, + { + "type": "MatrixElement", + "inputChannels": 3, + "outputChannels": 3, + "matrix": [0.4361, 0.3851, 0.1431, + 0.2225, 0.7169, 0.0606, + 0.0139, 0.0971, 0.7141], + "constants": [0.0, 0.0, 0.0] + } + ] + } + } +} +``` + +### MPE Element Types + +| `type` | Purpose | +|------------------------------|-------------------------------------------------| +| `CurveSetElement` | Per-channel independent curves | +| `MatrixElement` | Linear matrix with optional additive constants | +| `CLutElement` | N-dimensional colour lookup table | +| `ExtCLutElement` | CLUT with explicit storage type | +| `BAcsElement` / `EAcsElement`| Auxiliary colour space conversion bookends | +| `CalculatorElement` | Programmable calculator (iccMAX) | +| `XYZToJabElement` | CIECAM02 forward conversion | +| `JabToXYZElement` | CIECAM02 inverse conversion | +| `TintArrayElement` | Spectral tinting via a tag array | +| `EmissionMatrixElement` | Spectral-to-colorimetric matrix | +| `EmissionCLutElement` | Spectral CLUT | +| `EmissionObserverElement` | Observer spectral weighting | + +### CurveSetElement Curves + +Each entry in `"curves"` is a curve object with a `"type"` field: + +| `type` | Fields | +|-------------------------|--------------------------------------------------------| +| `SegmentedCurve` | `segments` array (same format as `segmentedCurveType`) | +| `SingleSampledCurve` | `firstEntry`, `lastEntry`, `samples`, `extensionType` | +| `SampledCalculatorCurve`| `firstEntry`, `lastEntry`, `desiredSize`, `calculator` | +| `DuplicateCurve` | `index` — source channel index (integer) | + +```json +"curves": [ + { + "type": "SegmentedCurve", + "segments": [ + { + "start": "-infinity", "end": "+infinity", + "type": "FormulaSegment", + "functionType": 0, + "parameters": [1.0, 0.5, 0.0, 0.0] + } + ] + }, + { "type": "DuplicateCurve", "index": 0 }, + { "type": "DuplicateCurve", "index": 0 } +] +``` + +--- + +## Complete Profile Examples + +### Simple Matrix/TRC Display Profile + +```json +{ + "IccProfile": { + "Header": { + "ProfileVersion": "4.4", + "ProfileDeviceClass": "mntr", + "DataColourSpace": "RGB ", + "PCS": "XYZ ", + "CreationDateTime": "2024-01-15T00:00:00", + "ProfileFileSignature": "acsp", + "PrimaryPlatform": "MSFT", + "CMMFlags": { "EmbeddedInFile": false, "UseWithEmbeddedDataOnly": false }, + "DeviceAttributes": { + "ReflectiveOrTransparency": "reflective", + "GlossyOrMatte": "glossy", + "MediaPolarity": "positive", + "MediaColour": "colour" + }, + "RenderingIntent": "Relative", + "PCSIlluminant": [0.9642, 1.0, 0.8249] + }, + "Tags": [ + { + "profileDescriptionTag": { + "data": { + "type": "multiLocalizedUnicodeType", + "localizedStrings": [ + { "language": "en", "country": "US", "text": "Simple sRGB Display Profile" } + ] + } + } + }, + { + "copyrightTag": { + "data": { "type": "textType", "text": "Public Domain" } + } + }, + { + "mediaWhitePointTag": { + "data": { "type": "XYZType", "XYZ": [0.9505, 1.0000, 1.0891] } + } + }, + { + "redMatrixColumnTag": { + "data": { "type": "XYZType", "XYZ": [0.4361, 0.2225, 0.0139] } + } + }, + { + "greenMatrixColumnTag": { + "data": { "type": "XYZType", "XYZ": [0.3851, 0.7169, 0.0971] } + } + }, + { + "blueMatrixColumnTag": { + "data": { "type": "XYZType", "XYZ": [0.1431, 0.0606, 0.7141] } + } + }, + { + "redTRCTag": { + "data": { + "type": "parametricCurveType", + "functionType": 3, + "params": [2.4, 0.9479, 0.0521, 0.0405, 0.0031] + } + } + }, + { "greenTRCTag": { "sameAs": "redTRCTag" } }, + { "blueTRCTag": { "sameAs": "redTRCTag" } } + ] + } +} +``` + +### Minimal Encoding-Class Profile + +Encoding profiles declare a colour space encoding without transformation data. + +```json +{ + "IccProfile": { + "Header": { + "ProfileVersion": "5.0", + "ProfileDeviceClass": "cenc", + "DataColourSpace": "RGB ", + "PCS": "XYZ ", + "CreationDateTime": "2024-01-15T00:00:00", + "ProfileFileSignature": "acsp", + "RenderingIntent": "Perceptual", + "PCSIlluminant": [0.9642, 1.0, 0.8249] + }, + "Tags": [ + { + "referenceNameTag": { + "data": { "type": "utf8TextType", "text": "sRGB" } + } + } + ] + } +} +``` + +### iccMAX MPE Profile (RGB → XYZ) + +```json +{ + "IccProfile": { + "Header": { + "ProfileVersion": "5.0", + "ProfileDeviceClass": "spac", + "DataColourSpace": "RGB ", + "PCS": "XYZ ", + "CreationDateTime": "2024-01-15T00:00:00", + "ProfileFileSignature": "acsp", + "PrimaryPlatform": "MSFT", + "CMMFlags": { "EmbeddedInFile": false, "UseWithEmbeddedDataOnly": false }, + "DeviceAttributes": { + "ReflectiveOrTransparency": "reflective", + "GlossyOrMatte": "glossy", + "MediaPolarity": "positive", + "MediaColour": "colour" + }, + "RenderingIntent": "Relative", + "PCSIlluminant": [0.9642, 1.0, 0.8249] + }, + "Tags": [ + { + "profileDescriptionTag": { + "data": { + "type": "multiLocalizedUnicodeType", + "localizedStrings": [ + { "language": "en", "country": "US", "text": "Example iccMAX Profile" } + ] + } + } + }, + { + "AToB1Tag": { + "data": { + "type": "multiProcessElementType", + "inputChannels": 3, + "outputChannels": 3, + "elements": [ + { + "type": "CurveSetElement", + "inputChannels": 3, + "outputChannels": 3, + "curves": [ + { + "type": "SegmentedCurve", + "segments": [ + { + "start": "-infinity", "end": 0.04045, + "type": "FormulaSegment", + "functionType": 0, + "parameters": [1.0, 0.07739938, 0.0125, 0.0] + }, + { + "start": 0.04045, "end": "+infinity", + "type": "FormulaSegment", + "functionType": 0, + "parameters": [2.4, 0.94786562, 0.05213438, 0.0031308] + } + ] + }, + { "type": "DuplicateCurve", "index": 0 }, + { "type": "DuplicateCurve", "index": 0 } + ] + }, + { + "type": "MatrixElement", + "inputChannels": 3, + "outputChannels": 3, + "matrix": [ + 0.4361, 0.3851, 0.1431, + 0.2225, 0.7169, 0.0606, + 0.0139, 0.0971, 0.7141 + ], + "constants": [0.0, 0.0, 0.0] + } + ] + } + } + } + ] + } +} +``` + +--- + +## Tag Structures (`tagStructType`) + +iccMAX struct tags contain named member tags. Each member in `"memberTags"` is a +single-key object whose key is the element name and whose value is a flat tag object +with a `"type"` field (same `type` values as top-level tag data). + +```json +{ + "colorEncodingParamsTag": { + "data": { + "type": "tagStructType", + "structureType": "colorEncodingParamsStruct", + "memberTags": [ + { + "blueColorantTag": { + "type": "XYZType", + "XYZ": [0.1431, 0.0606, 0.7141] + } + } + ] + } + } +} +``` + +--- + +## Tag Arrays (`tagArrayType`) + +iccMAX array tags contain an ordered list of typed tag objects in `"arrayTags"`. +Each element is a flat object with a `"type"` field (same `type` values as +top-level tag data). + +```json +{ + "spectralViewingConditionsTag": { + "data": { + "type": "tagArrayType", + "arrayType": "namedColorArray", + "arrayTags": [ + { + "type": "utf8TextType", + "text": "D50" + } + ] + } + } +} +``` + +--- + +## Signatures + +Signature fields (4-character ICC codes) appear in several tag types and the profile +header. Four formats are accepted: + +| Format | Example | Meaning | +|---------------|--------------|--------------------------------------------| +| Empty string | "" | Zero / unset signature | +| 4 characters | "RGB " | Direct four-character code (space-padded) | +| 6 characters | "rs0024" | Two-char prefix + 4 hex digits | +| 8 hex digits | "73663031" | Full signature as hex | + +--- + +## Tips and Common Mistakes + +- **Always include the `"IccProfile"` wrapper** — the top-level key is `"IccProfile"`, + not `"Header"` or `"Tags"` directly. +- **XYZ values are arrays, not objects** — write `[0.95, 1.0, 1.09]` not + `{ "X": 0.95, "Y": 1.0, "Z": 1.09 }`. +- **`"type"` is always required** on `data` objects, MPE elements, curve-set curves, + MBB curves, and struct member tags. Missing `"type"` is the most common parse error. +- **`curveType` tag needs the subtype** — a curveType tag requires both `"type": "curveType"` + (the ICC tag type) and `"curveType": "gamma"` (the subtype selector). +- **MBB curve types are `"Curve"`, `"ParametricCurve"`, `"SegmentedCurve"`** — not + `"curveType"` / `"parametricCurveType"` / `"segmentedCurveType"`. +- **Segment types are PascalCase** — `"FormulaSegment"` and `"SampledSegment"`, + not `"formulaSegment"` / `"sampledSegment"`. +- **`parametricCurveType` uses `"params"`** — not `"Parameters"` or `"parameters"`. +- **Segment boundaries must be contiguous** — segments must form a non-overlapping + cover of the real line. Use `"-infinity"` / `"+infinity"` for the outermost ends. +- **`sameAs` must come after the original** — the referenced tag key must appear + earlier in the `Tags` array. +- **Validation before use** — always run `IccDumpProfile` after building a profile + with `IccFromJson` to catch structural issues before the profile is deployed. +- **Round-trip inspection** — convert an existing reference profile with `IccToJson` + first to see the expected JSON structure for a given profile class. diff --git a/docs/index.md b/docs/index.md index 38c9577f5..395d090f7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -45,6 +45,16 @@ Within the project are several libraries and tools as follows: IccFromXML tool provides a simple direct method to create and manipulate ICC and iccMAX profiles. + * IccToJson is a cross platform command line tool that converts a binary ICC or iccMAX + profile to a JSON representation. The resulting JSON file can be inspected or edited + and then converted back to a binary profile using IccFromJson. + + * IccFromJson is a cross platform command line tool that creates a binary ICC or iccMAX + profile from a JSON file. A [JSON Schema](icc-profile.schema.json) is provided that + describes the format and enables inline validation in supporting editors. + See the [IccJSON guide](iccjson.md) for full documentation on the JSON format and + worked examples. + * IccApplyNamedCmm is a cross platform command line tool that allows a sequence of ICC profile formats and/or iccMAX profile formats to be applied to colors defined in a text based input profile outputting the results to the console, and can