From 5be507a97f1fa509fe9898315a33b418da506dea Mon Sep 17 00:00:00 2001 From: h_vogt Date: Wed, 30 Sep 2009 22:22:36 +0000 Subject: [PATCH] command sysinfo --- ChangeLog | 4 + src/frontend/Makefile.am | 5 +- src/frontend/com_sysinfo.c | 492 ++ src/frontend/commands.c | 4 + src/include/fteext.h | 2 + visualc/vngspice.sln | 4 +- visualc/vngspice.vcproj | 16264 ++++++++++++++++++----------------- 7 files changed, 8645 insertions(+), 8130 deletions(-) create mode 100644 src/frontend/com_sysinfo.c diff --git a/ChangeLog b/ChangeLog index 32663dc09..e92e1f359 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2009-10-01 Holger Vogt + * /frontend com_sysinfo.c, commands.c, fteext.h, makefile.am: + command sysinfo added + 2009-09-26 Holger Vogt * windisp.c, compatmode.c, compatmode.h: CVS header added (still not working with compatmode) diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index 4306baf69..eba62a0b9 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -62,8 +62,9 @@ libfte_la_SOURCES = \ com_fft.h \ com_state.c \ com_state.h \ - com_strcmp.c \ - com_strcmp.h \ + com_strcmp.c \ + com_strcmp.h \ + com_sysinfo.c \ com_unset.c \ com_unset.h \ com_xgraph.c \ diff --git a/src/frontend/com_sysinfo.c b/src/frontend/com_sysinfo.c new file mode 100644 index 000000000..93a4bc0cc --- /dev/null +++ b/src/frontend/com_sysinfo.c @@ -0,0 +1,492 @@ + /* Provide system information + + LINUX: /proc file system + Windows: GlobalMemoryStatusEx, GetSystemInfo, GetVersionExA, RegQueryValueExA + + Authors: Holger Vogt, Hendrik Vogt + + $Id$ + */ + +#include "config.h" +#include "ngspice.h" +#include "cpdefs.h" + +/* We might compile for Windows, but only as a console application (e.g. tcl) */ +#if defined(HAS_WINDOWS) || defined(__MINGW32__) || defined(_MSC_VER) +#define HAVE_WIN32 +#endif + +#ifdef HAVE_WIN32 + +#define WIN32_LEAN_AND_MEAN + +#ifdef __MINGW32__ /* access to GlobalMemoryStatusEx in winbase.h:1558 */ +#define WINVER 0x0500 +#endif + +#undef BOOLEAN +#include "windows.h" +#if defined(__MINGW32__) || (_MSC_VER > 1200) /* Exclude VC++ 6.0 from using the psapi */ +#include +#endif +#endif + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +#define tInt int +#define TesError int +#define TES_FAIL 1 +#define TES_SUCCESS 0 + +/* system info */ +typedef struct TSI { + char* cpuModelName; + int numPhysicalProcessors; + int numLogicalProcessors; + char* osName; +} TesSystemInfo; + +/* memory info */ +static struct sys_memory { + unsigned long int size; /* Total memory size */ + unsigned long int free; /* Free memory */ + unsigned long int swap_t; /* Swap total */ + unsigned long int swap_f; /* Swap free */ +}; + +static struct sys_memory mem_t_act; + +TesError tesCreateSystemInfo(TesSystemInfo *info); +static size_t get_sysmem(struct sys_memory *memall); + +/* Print to stream the given memory size in a human friendly format */ +static void +fprintmem(FILE* stream, unsigned long int memory) { + if (memory > 1048576) + fprintf(stream, "%8.6f MB", memory/1048576.); + else if (memory > 1024) + fprintf(stream, "%5.3f kB", memory/1024.); + else + fprintf(stream, "%lu bytes", memory); +} + + +static void tesFreeSystemInfo(TesSystemInfo *info) { + if(info != NULL) { + free(info->cpuModelName); + free(info->osName); + } +} + +/* print system info */ +void com_sysinfo(void) +{ + int errorcode; + TesSystemInfo* info; + + info = (TesSystemInfo*)tmalloc(sizeof(TesSystemInfo)); + + errorcode = tesCreateSystemInfo(info); + if (errorcode) + fprintf(cp_err, "No system info available! \n"); + else { + fprintf(cp_out, "\nOS: %s\n", info->osName); + fprintf(cp_out, "CPU: %s\n", info->cpuModelName); + if (info->numPhysicalProcessors > 0) + fprintf(cp_out, "Physical processors: %d, ", info->numPhysicalProcessors); + fprintf(cp_out, "Logical processors: %d\n", + info->numLogicalProcessors); + } +#if defined(HAVE_WIN32) || defined(HAVE__PROC_MEMINFO) + + get_sysmem(&mem_t_act); + + /* get_sysmem returns bytes */ + fprintf(cp_out, "Total DRAM available = "); + fprintmem(cp_out, mem_t_act.size); + fprintf(cp_out, ".\n"); + + fprintf(cp_out, "DRAM currently available = "); + fprintmem(cp_out, mem_t_act.free); + fprintf(cp_out, ".\n\n"); + +#endif + +/* +Console.WriteLine("Number of CPUs: {0}", Environment.GetEnvironmentVariable _ + ("NUMBER_OF_PROCESSORS")) +Console.WriteLine("CPU Architecture: {0}", Environment.GetEnvironmentVariable _ + ("PROCESSOR_ARCHITECTURE")) +Console.WriteLine("CPU Identifier: {0}", Environment.GetEnvironmentVariable _ + ("PROCESSOR_IDENTIFIER")) +Console.WriteLine("CPU Level: {0}", Environment.GetEnvironmentVariable _ + ("PROCESSOR_LEVEL")) +Console.WriteLine("CPU Revision: {0}", Environment.GetEnvironmentVariable _ + ("PROCESSOR_REVISION")) + +*/ + tesFreeSystemInfo(info); + tfree(info); +} + + +#ifdef HAVE__PROC_MEMINFO + +/* Get memory information */ +static size_t get_sysmem(struct sys_memory *memall) { + FILE *fp; + char buffer[2048]; + size_t bytes_read; + char *match; + long mem_got; + + if((fp = fopen("/proc/meminfo", "r")) == NULL) { + perror("fopen(\"/proc/meminfo\")"); + return 0; + } + + bytes_read = fread (buffer, 1, sizeof (buffer), fp); + fclose (fp); + if (bytes_read == 0 || bytes_read == sizeof (buffer)) + return 0; + buffer[bytes_read] = '\0'; + + /* Search for string "MemTotal" */ + match = strstr (buffer, "MemTotal"); + if (match == NULL) /* not found */ + return 0; + sscanf (match, "MemTotal: %ld", &mem_got); + memall->size = mem_got*1024; /* 1MB = 1024KB */ + /* Search for string "MemFree" */ + match = strstr (buffer, "MemFree"); + if (match == NULL) /* not found */ + return 0; + sscanf (match, "MemFree: %ld", &mem_got); + memall->free = mem_got*1024; /* 1MB = 1024KB */ + /* Search for string "SwapTotal" */ + match = strstr (buffer, "SwapTotal"); + if (match == NULL) /* not found */ + return 0; + sscanf (match, "SwapTotal: %ld", &mem_got); + memall->swap_t = mem_got*1024; /* 1MB = 1024KB */ + /* Search for string "SwapFree" */ + match = strstr (buffer, "SwapFree"); + if (match == NULL) /* not found */ + return 0; + sscanf (match, "SwapFree: %ld", &mem_got); + memall->swap_f = mem_got*1024; /* 1MB = 1024KB */ + return 1; +} + + + +/* Return length of first line in a string */ +tInt getLineLength(const char *str) { + tInt length = strlen(str); + char c = str[0]; + tInt index = 0; + + while((c != '\n') && (index < length)) { + index++; + c = str[index]; + } + return index; +} + +/* Checks if number 'match' is found in a vector 'set' of size 'size' + Returns 1 if yes, otherwise, 0 */ +tInt searchInSet(const tInt *set, tInt size, tInt match) { + tInt index; + for(index = 0; index < size; index++) { + if(match == set[index]) { + return 1; + } + } + return 0; +} + +TESAPI void TESAPIENTRY tesFreeSystemInfo(TesSystemInfo *info) { + if(info != NULL) { + free(info->cpuModelName); + free(info->osName); + } +} + +/* Get system information */ +TESAPI TesError TESAPIENTRY tesCreateSystemInfo(TesSystemInfo *info) { + FILE *file; + TesError error = TES_SUCCESS; + + if(info == NULL) + return TES_INVALID_PARAMS; + info->cpuModelName = NULL; + info->osName = NULL; + info->numLogicalProcessors = info->numPhysicalProcessors = 0; + + /* get kernel version string */ + file = fopen("/proc/version", "rb"); + if(file != NULL) { + tInt size = 0; + char buf; + + /* read bytes and find end of file */ + buf = fgetc(file); + while(buf != EOF) { + size++; + buf = fgetc(file); + } + + info->osName = malloc((size) * sizeof(char)); + rewind(file); + fread(info->osName, sizeof(char), size, file); + fclose(file); + + info->osName[size-1] = '\0'; + } + else { + error = TES_FAIL; + } + + /* get cpu information */ + file = fopen("/proc/cpuinfo", "rb"); + if(file != NULL) { + tInt size = 0; + char buf, *inStr; + + /* read bytes and find end of file */ + buf = fgetc(file); + while(buf != EOF) { + size++; + buf = fgetc(file); + } + /* get complete string */ + inStr = malloc((size+1) * sizeof(char)); + rewind(file); + fread(inStr, sizeof(char), size, file); + inStr[size] = '\0'; + + { + const char *matchStr = "model name"; + /* pointer to first occurrence of model name*/ + const char *modelStr = strstr(inStr, matchStr); + if(modelStr != NULL) { + /* search for ':' */ + const char *modelPtr = strchr(modelStr, ':'); + if(modelPtr != NULL) { + /*length of string from ':' till end of line */ + tInt numToEOL = getLineLength(modelPtr); + if(numToEOL > 2) { + /* skip ": "*/ + numToEOL-=2; + info->cpuModelName = malloc(numToEOL+1); + memcpy(info->cpuModelName, modelPtr+2, numToEOL); + info->cpuModelName[numToEOL] = '\0'; + } + } + else { + error = TES_FAIL; + } + } + else { + error = TES_FAIL; + } + } + { + const char *matchStrProc = "processor"; + const char *matchStrPhys = "physical id"; + char *strPtr = inStr; + tInt numProcs = 0; + tInt *physIDs; + + /* get number of logical processors */ + while((strPtr = strstr(strPtr, matchStrProc)) != NULL) { + numProcs++; + strPtr += strlen(matchStrProc); + } + info->numLogicalProcessors = numProcs; + physIDs = malloc(numProcs * sizeof(tInt)); + + /* get number of physical CPUs */ + numProcs = 0; + strPtr = inStr; + while((strPtr = strstr(strPtr, matchStrProc)) != NULL) { + + /* search for first occurrence of physical id */ + strPtr = strstr(strPtr, matchStrPhys); + if(strPtr != NULL) { + /* go to ';' */ + strPtr = strchr(strPtr, ':'); + if(strPtr != NULL) { + tInt buffer = 0; + /* skip ": " */ + strPtr += 2; + /* get number */ + sscanf(strPtr, "%d", &buffer); + /* If this physical id is unique, + we have another physically available CPU */ + if(searchInSet(physIDs, numProcs, buffer) == 0) { + physIDs[numProcs] = buffer; + numProcs++; + } + } + else { +// error = TES_FAIL; + break; + } + } + else { +// error = TES_FAIL; + break; + } + } + info->numPhysicalProcessors = numProcs; + free(physIDs); + } + + /* another test to get number of logical processors */ + if {info->numLogicalProcessors == 0) { + char* token; + char* cpustr = copy(inStr); + while (cpustr) + if cieq(gettok(&cpustr), "processor") { + gettok(&cpustr); + token = gettok(&cpustr); + } + + info->numLogicalProcessors = atoi(token) + 1; + tfree cpustr; + } + + free(inStr); + fclose(file); + } + else { + error = TES_FAIL; + } + + return error; +} + +#elif HAS_WINDOWS + +/* get memory information */ +static size_t get_sysmem(struct sys_memory *memall) { +#if ( _WIN32_WINNT >= 0x0500) + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx( &ms); + memall->size = ms.ullTotalPhys; + memall->free = ms.ullAvailPhys; + memall->swap_t = ms.ullTotalPageFile; + memall->swap_f = ms.ullAvailPageFile; +#else + MEMORYSTATUS ms; + ms.dwLength = sizeof(MEMORYSTATUS); + GlobalMemoryStatus( &ms); + memall->size = ms.dwTotalPhys; + memall->free = ms.dwAvailPhys; + memall->swap_t = ms.dwTotalPageFile; + memall->swap_f = ms.dwAvailPageFile; +#endif /*_WIN32_WINNT 0x0500*/ + return 1; +} + +/* get system information */ +TesError tesCreateSystemInfo(TesSystemInfo *info) { + OSVERSIONINFOA version; + char *versionStr = NULL, *procStr, *freeStr; + tInt major, minor; + DWORD dwLen; + HKEY hkBaseCPU; + LONG lResult; + + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + + + info->numPhysicalProcessors = 0; + info->numLogicalProcessors = sysinfo.dwNumberOfProcessors; //atoi(getenv("NUMBER_OF_PROCESSORS")); + info->osName = NULL; + + version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if(GetVersionExA(&version) == 0) { + return TES_FAIL; + } + major = version.dwMajorVersion; + minor = version.dwMinorVersion; + switch(major) { + case 4: + if(minor == 0) { + versionStr = "Windows 95/NT4.0"; + } + else if(minor == 10) { + versionStr = "Windows 98"; + } + else if (minor == 90) { + versionStr = "Windows ME"; + } + break; + case 5: + if(minor == 0) { + versionStr = "Windows 2000"; + } + else if(minor == 1) { + versionStr = "Windows XP"; + } + else if(minor == 2) { + versionStr = "Windows Server 2003"; + } + break; + case 6: + if(minor == 0) { + versionStr = "Windows Vista"; + } + break; + default: + break; + } + + if(versionStr != NULL) { + tInt lengthCSD = strlen(version.szCSDVersion); + tInt lengthVer = strlen(versionStr); + + info->osName = malloc(lengthVer + lengthCSD + 2); + memcpy(info->osName, versionStr, lengthVer); + memcpy(info->osName + lengthVer + 1, version.szCSDVersion, lengthCSD); + info->osName[lengthVer] = ' '; + info->osName[lengthVer + lengthCSD + 1] = '\0'; + } + + + lResult = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + 0,KEY_READ,&hkBaseCPU); + if(lResult != ERROR_SUCCESS) { + info->cpuModelName = NULL; + return TES_FAIL; + } + + RegQueryValueExA(hkBaseCPU,"ProcessorNameString",0,0,NULL,&dwLen); + freeStr = procStr = tmalloc(dwLen+1); + RegQueryValueExA(hkBaseCPU,"ProcessorNameString",0,0,(LPBYTE)procStr,&dwLen); + procStr[dwLen] = '\0'; + while (*procStr == ' ') procStr++; + info->cpuModelName = copy(procStr); + tfree(freeStr); + + RegCloseKey(hkBaseCPU); + + return TES_SUCCESS; +} + +#else +/* no Windows OS, no proc info file system */ +TesError tesCreateSystemInfo(TesSystemInfo *info) { + return 1; +} + +#endif + diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 7c4159799..1a05e5df1 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -287,6 +287,10 @@ struct comm spcp_coms[] = { { 040, 040, 040, 040 }, E_DEFHMASK, 0, LOTS, (void (*)()) NULL, "models ... : parameters ... : Print out model summary." } , + { "sysinfo", com_sysinfo, FALSE, TRUE, FALSE, + { 040, 040, 040, 040 }, E_DEFHMASK, 0, LOTS, + (void (*)()) NULL, + "Print out system info summary." } , { "alter", com_alter, FALSE, TRUE, FALSE, { 040, 040, 040, 040 }, E_DEFHMASK, 0, LOTS, (void (*)()) NULL, diff --git a/src/include/fteext.h b/src/include/fteext.h index 098deb697..2e37f746b 100644 --- a/src/include/fteext.h +++ b/src/include/fteext.h @@ -231,6 +231,8 @@ extern void com_spec(); /* com_fft.c */ extern void com_fft(wordlist*); +/* com_sysinfo.c */ +extern void com_sysinfo(void); /* ginterface.c */ extern bool gi_init(); diff --git a/visualc/vngspice.sln b/visualc/vngspice.sln index 5c09817db..4779f4bb6 100644 --- a/visualc/vngspice.sln +++ b/visualc/vngspice.sln @@ -11,8 +11,8 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.Debug|Win32.ActiveCfg = Debug|Win32 {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.Debug|Win32.Build.0 = Debug|Win32 - {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.Release|Win32.ActiveCfg = Debug|Win32 - {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.Release|Win32.Build.0 = Debug|Win32 + {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.Release|Win32.ActiveCfg = Release|Win32 + {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/visualc/vngspice.vcproj b/visualc/vngspice.vcproj index f7c7b2d79..9d3d71f61 100644 --- a/visualc/vngspice.vcproj +++ b/visualc/vngspice.vcproj @@ -1,8126 +1,8138 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +