This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new 0f3776509 industry/cnc: Add nvmpg-dro application for the NVMPG
pendant.
0f3776509 is described below
commit 0f3776509d1e6709b162dffff477f4142866bae3
Author: Alan Carvalho de Assis <[email protected]>
AuthorDate: Mon Oct 12 15:29:28 2026 -0300
industry/cnc: Add nvmpg-dro application for the NVMPG pendant.
Add a CNC category under industry and the nvmpg-dro application:
a replacement firmware for the Digital Dream NVMPG CNC pendant
(nvmpg-stm32f1 board) used with the NVUM/NVEM/EC300/EC500 motion
controllers.
It speaks the same serial protocol as the original firmware: it
receives the 51-byte status frames from the controller, answers the
identify request, draws the axis positions, overrides, MPG step and
lamps on the LCD (redrawing only what changed), and reports the key
presses and releases back to the controller. Optionally it shows
the NuttX logo at startup.
Signed-off-by: Alan Carvalho de Assis <[email protected]>
Assisted-by: Claude Opus 5.5
---
industry/cnc/.gitignore | 1 +
industry/cnc/CMakeLists.txt | 25 ++
industry/cnc/Make.defs | 23 ++
industry/cnc/Makefile | 25 ++
industry/cnc/nvmpg-dro/CMakeLists.txt | 39 ++
industry/cnc/nvmpg-dro/Kconfig | 59 +++
industry/cnc/nvmpg-dro/Make.defs | 25 ++
industry/cnc/nvmpg-dro/Makefile | 39 ++
industry/cnc/nvmpg-dro/nvmpg_dro.h | 102 +++++
industry/cnc/nvmpg-dro/nvmpg_dro_lcd.c | 352 ++++++++++++++++
industry/cnc/nvmpg-dro/nvmpg_dro_logo.c | 707 ++++++++++++++++++++++++++++++++
industry/cnc/nvmpg-dro/nvmpg_dro_main.c | 601 +++++++++++++++++++++++++++
12 files changed, 1998 insertions(+)
diff --git a/industry/cnc/.gitignore b/industry/cnc/.gitignore
new file mode 100644
index 000000000..9e1d2593e
--- /dev/null
+++ b/industry/cnc/.gitignore
@@ -0,0 +1 @@
+/Kconfig
diff --git a/industry/cnc/CMakeLists.txt b/industry/cnc/CMakeLists.txt
new file mode 100644
index 000000000..a45675193
--- /dev/null
+++ b/industry/cnc/CMakeLists.txt
@@ -0,0 +1,25 @@
+#
##############################################################################
+# apps/industry/cnc/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
contributor
+# license agreements. See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership. The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may
not
+# use this file except in compliance with the License. You may obtain a copy
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+#
##############################################################################
+
+nuttx_add_subdirectory()
+
+nuttx_generate_kconfig(MENUDESC "CNC")
diff --git a/industry/cnc/Make.defs b/industry/cnc/Make.defs
new file mode 100644
index 000000000..f828252a8
--- /dev/null
+++ b/industry/cnc/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/industry/cnc/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(wildcard $(APPDIR)/industry/cnc/*/Make.defs)
diff --git a/industry/cnc/Makefile b/industry/cnc/Makefile
new file mode 100644
index 000000000..36d483c83
--- /dev/null
+++ b/industry/cnc/Makefile
@@ -0,0 +1,25 @@
+############################################################################
+# apps/industry/cnc/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+MENUDESC = "CNC"
+
+include $(APPDIR)/Directory.mk
diff --git a/industry/cnc/nvmpg-dro/CMakeLists.txt
b/industry/cnc/nvmpg-dro/CMakeLists.txt
new file mode 100644
index 000000000..ec803119d
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
##############################################################################
+# apps/industry/cnc/nvmpg-dro/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
contributor
+# license agreements. See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership. The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may
not
+# use this file except in compliance with the License. You may obtain a copy
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+#
##############################################################################
+
+if(CONFIG_INDUSTRY_CNC_NVMPG_DRO)
+ set(SRCS nvmpg_dro_main.c nvmpg_dro_lcd.c)
+
+ if(CONFIG_INDUSTRY_CNC_NVMPG_DRO_SPLASH)
+ list(APPEND SRCS nvmpg_dro_logo.c)
+ endif()
+
+ nuttx_add_application(
+ NAME
+ ${CONFIG_INDUSTRY_CNC_NVMPG_DRO_PROGNAME}
+ SRCS
+ ${SRCS}
+ STACKSIZE
+ ${CONFIG_INDUSTRY_CNC_NVMPG_DRO_STACKSIZE}
+ PRIORITY
+ ${CONFIG_INDUSTRY_CNC_NVMPG_DRO_PRIORITY})
+endif()
diff --git a/industry/cnc/nvmpg-dro/Kconfig b/industry/cnc/nvmpg-dro/Kconfig
new file mode 100644
index 000000000..52f3a5209
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/Kconfig
@@ -0,0 +1,59 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config INDUSTRY_CNC_NVMPG_DRO
+ tristate "Digital Dream NVMPG pendant DRO"
+ default n
+ depends on LCD_DEV && DEV_GPIO && !DISABLE_PTHREAD
+ ---help---
+ Digital readout firmware for the Digital Dream NVMPG pendant
used
+ with the NVUM/NVEM/EC300/EC500 CNC motion controllers. It shows
+ the axis positions, overrides and MPG step that the controller
+ sends over the serial link, and reports the pendant keys back to
+ it using the same protocol as the original firmware.
+
+if INDUSTRY_CNC_NVMPG_DRO
+
+config INDUSTRY_CNC_NVMPG_DRO_PROGNAME
+ string "Program name"
+ default "nvmpg_dro"
+
+config INDUSTRY_CNC_NVMPG_DRO_PRIORITY
+ int "DRO task priority"
+ default 100
+
+config INDUSTRY_CNC_NVMPG_DRO_STACKSIZE
+ int "DRO stack size"
+ default 2048
+
+config INDUSTRY_CNC_NVMPG_DRO_TTY
+ string "Serial port connected to the controller"
+ default "/dev/ttyS0"
+
+config INDUSTRY_CNC_NVMPG_DRO_LCD
+ string "LCD character device"
+ default "/dev/lcd0"
+
+config INDUSTRY_CNC_NVMPG_DRO_NKEYS
+ int "Number of keys (/dev/gpio0 .. /dev/gpioN-1)"
+ default 10
+
+config INDUSTRY_CNC_NVMPG_DRO_SPLASH
+ bool "Show the NuttX logo at startup"
+ default y
+
+config INDUSTRY_CNC_NVMPG_DRO_SPLASH_MS
+ int "Splash duration (ms)"
+ default 1000
+ depends on INDUSTRY_CNC_NVMPG_DRO_SPLASH
+
+config INDUSTRY_CNC_NVMPG_DRO_LINK_TIMEOUT
+ int "Link timeout (ms)"
+ default 1000
+ ---help---
+ Positions are drawn greyed out when no frame was received from
+ the controller for this long.
+
+endif
diff --git a/industry/cnc/nvmpg-dro/Make.defs b/industry/cnc/nvmpg-dro/Make.defs
new file mode 100644
index 000000000..9d2726d00
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/industry/cnc/nvmpg-dro/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifneq ($(CONFIG_INDUSTRY_CNC_NVMPG_DRO),)
+CONFIGURED_APPS += $(APPDIR)/industry/cnc/nvmpg-dro
+endif
diff --git a/industry/cnc/nvmpg-dro/Makefile b/industry/cnc/nvmpg-dro/Makefile
new file mode 100644
index 000000000..e37eba589
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/Makefile
@@ -0,0 +1,39 @@
+############################################################################
+# apps/industry/cnc/nvmpg-dro/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+# NVMPG DRO application info
+
+PROGNAME = $(CONFIG_INDUSTRY_CNC_NVMPG_DRO_PROGNAME)
+PRIORITY = $(CONFIG_INDUSTRY_CNC_NVMPG_DRO_PRIORITY)
+STACKSIZE = $(CONFIG_INDUSTRY_CNC_NVMPG_DRO_STACKSIZE)
+MODULE = $(CONFIG_INDUSTRY_CNC_NVMPG_DRO)
+
+MAINSRC = nvmpg_dro_main.c
+CSRCS = nvmpg_dro_lcd.c
+
+ifeq ($(CONFIG_INDUSTRY_CNC_NVMPG_DRO_SPLASH),y)
+CSRCS += nvmpg_dro_logo.c
+endif
+
+include $(APPDIR)/Application.mk
diff --git a/industry/cnc/nvmpg-dro/nvmpg_dro.h
b/industry/cnc/nvmpg-dro/nvmpg_dro.h
new file mode 100644
index 000000000..0e0e9e74f
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/nvmpg_dro.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+ * apps/industry/cnc/nvmpg-dro/nvmpg_dro.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_INDUSTRY_CNC_NVMPG_DRO_NVMPG_DRO_H
+#define __APPS_INDUSTRY_CNC_NVMPG_DRO_NVMPG_DRO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* RGB565 colors */
+
+#define NVDRO_BLACK 0x0000
+#define NVDRO_WHITE 0xffff
+#define NVDRO_RED 0xf800
+#define NVDRO_GREEN 0x07e0
+#define NVDRO_YELLOW 0xffe0
+#define NVDRO_BG 0x3a6f /* Blue-violet background */
+#define NVDRO_PANEL 0xa7f4 /* Light green "LCD" panel */
+#define NVDRO_BOX 0xce59 /* Light grey label boxes */
+#define NVDRO_DIM 0x6b6d /* Greyed out text */
+#define NVDRO_LAMP_OFF 0x2104
+
+/* Glyph geometry of the built-in 5x7 font at scale 1 */
+
+#define NVDRO_FONT_W 5
+#define NVDRO_FONT_H 7
+
+/* The NuttX logo is a 160x160 image */
+
+#define NVDRO_LOGO_SIZE 160
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/* Open the LCD and query its geometry. Returns 0 or a negated errno. */
+
+int nvdro_lcd_open(FAR const char *path);
+void nvdro_lcd_close(void);
+int nvdro_lcd_width(void);
+int nvdro_lcd_height(void);
+
+/* Fill the rectangle with its top-left corner at (x, y) */
+
+void nvdro_fill(int x, int y, int w, int h, uint16_t color);
+
+/* Copy one row of w pixels to the screen at (x, y) */
+
+void nvdro_putrow(int x, int y, int w, FAR const uint16_t *pixels);
+
+/* Draw a hollow rectangle with a border 't' pixels thick */
+
+void nvdro_frame(int x, int y, int w, int h, int t, uint16_t color);
+
+/* Draw a string with the 5x7 font magnified 'scale' times. Each character
+ * cell is (5 * scale + gap) pixels wide and 7 * scale pixels tall, and the
+ * background of the cell is painted too, so redrawing a string erases
+ * what was there. Returns the width drawn.
+ */
+
+int nvdro_text(int x, int y, FAR const char *str, int scale, int gap,
+ uint16_t fg, uint16_t bg);
+
+/* Width in pixels of 'nchars' characters at the given scale and gap */
+
+int nvdro_text_width(int nchars, int scale, int gap);
+
+#ifdef CONFIG_INDUSTRY_CNC_NVMPG_DRO_SPLASH
+/* Clear the screen and draw the NuttX logo centered */
+
+void nvdro_logo(void);
+#endif
+
+#endif /* __APPS_INDUSTRY_CNC_NVMPG_DRO_NVMPG_DRO_H */
diff --git a/industry/cnc/nvmpg-dro/nvmpg_dro_lcd.c
b/industry/cnc/nvmpg-dro/nvmpg_dro_lcd.c
new file mode 100644
index 000000000..75ae66064
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/nvmpg_dro_lcd.c
@@ -0,0 +1,352 @@
+/****************************************************************************
+ * apps/industry/cnc/nvmpg-dro/nvmpg_dro_lcd.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <nuttx/lcd/lcd_dev.h>
+#include <nuttx/video/fb.h>
+
+#include "nvmpg_dro.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Scratch buffer for one character cell or a band of a filled rectangle */
+
+#define NVDRO_BUFPIXELS 1024
+
+#define NVDRO_FONT_FIRST 0x20
+#define NVDRO_FONT_LAST 0x5a
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static int g_lcdfd = -1;
+static int g_xres;
+static int g_yres;
+static uint16_t g_buf[NVDRO_BUFPIXELS];
+
+/* Classic 5x7 font, ' ' .. 'Z'. One byte per column, bit 0 is the top
+ * row. Lowercase letters are drawn as uppercase except 'x', which is
+ * handled separately so that the step reads "x100" like on the original
+ * pendant.
+ */
+
+static const uint8_t g_font[][NVDRO_FONT_W] =
+{
+ { 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ' ' */
+ { 0x00, 0x00, 0x5f, 0x00, 0x00 }, /* '!' */
+ { 0x00, 0x07, 0x00, 0x07, 0x00 }, /* '"' */
+ { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, /* '#' */
+ { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, /* '$' */
+ { 0x23, 0x13, 0x08, 0x64, 0x62 }, /* '%' */
+ { 0x36, 0x49, 0x55, 0x22, 0x50 }, /* '&' */
+ { 0x00, 0x05, 0x03, 0x00, 0x00 }, /* ''' */
+ { 0x00, 0x1c, 0x22, 0x41, 0x00 }, /* '(' */
+ { 0x00, 0x41, 0x22, 0x1c, 0x00 }, /* ')' */
+ { 0x08, 0x2a, 0x1c, 0x2a, 0x08 }, /* '*' */
+ { 0x08, 0x08, 0x3e, 0x08, 0x08 }, /* '+' */
+ { 0x00, 0x50, 0x30, 0x00, 0x00 }, /* ',' */
+ { 0x08, 0x08, 0x08, 0x08, 0x08 }, /* '-' */
+ { 0x00, 0x60, 0x60, 0x00, 0x00 }, /* '.' */
+ { 0x20, 0x10, 0x08, 0x04, 0x02 }, /* '/' */
+ { 0x3e, 0x51, 0x49, 0x45, 0x3e }, /* '0' */
+ { 0x00, 0x42, 0x7f, 0x40, 0x00 }, /* '1' */
+ { 0x42, 0x61, 0x51, 0x49, 0x46 }, /* '2' */
+ { 0x21, 0x41, 0x45, 0x4b, 0x31 }, /* '3' */
+ { 0x18, 0x14, 0x12, 0x7f, 0x10 }, /* '4' */
+ { 0x27, 0x45, 0x45, 0x45, 0x39 }, /* '5' */
+ { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, /* '6' */
+ { 0x01, 0x71, 0x09, 0x05, 0x03 }, /* '7' */
+ { 0x36, 0x49, 0x49, 0x49, 0x36 }, /* '8' */
+ { 0x06, 0x49, 0x49, 0x29, 0x1e }, /* '9' */
+ { 0x00, 0x36, 0x36, 0x00, 0x00 }, /* ':' */
+ { 0x00, 0x56, 0x36, 0x00, 0x00 }, /* ';' */
+ { 0x00, 0x08, 0x14, 0x22, 0x41 }, /* '<' */
+ { 0x14, 0x14, 0x14, 0x14, 0x14 }, /* '=' */
+ { 0x41, 0x22, 0x14, 0x08, 0x00 }, /* '>' */
+ { 0x02, 0x01, 0x51, 0x09, 0x06 }, /* '?' */
+ { 0x32, 0x49, 0x79, 0x41, 0x3e }, /* '@' */
+ { 0x7e, 0x11, 0x11, 0x11, 0x7e }, /* 'A' */
+ { 0x7f, 0x49, 0x49, 0x49, 0x36 }, /* 'B' */
+ { 0x3e, 0x41, 0x41, 0x41, 0x22 }, /* 'C' */
+ { 0x7f, 0x41, 0x41, 0x22, 0x1c }, /* 'D' */
+ { 0x7f, 0x49, 0x49, 0x49, 0x41 }, /* 'E' */
+ { 0x7f, 0x09, 0x09, 0x01, 0x01 }, /* 'F' */
+ { 0x3e, 0x41, 0x41, 0x51, 0x32 }, /* 'G' */
+ { 0x7f, 0x08, 0x08, 0x08, 0x7f }, /* 'H' */
+ { 0x00, 0x41, 0x7f, 0x41, 0x00 }, /* 'I' */
+ { 0x20, 0x40, 0x41, 0x3f, 0x01 }, /* 'J' */
+ { 0x7f, 0x08, 0x14, 0x22, 0x41 }, /* 'K' */
+ { 0x7f, 0x40, 0x40, 0x40, 0x40 }, /* 'L' */
+ { 0x7f, 0x02, 0x04, 0x02, 0x7f }, /* 'M' */
+ { 0x7f, 0x04, 0x08, 0x10, 0x7f }, /* 'N' */
+ { 0x3e, 0x41, 0x41, 0x41, 0x3e }, /* 'O' */
+ { 0x7f, 0x09, 0x09, 0x09, 0x06 }, /* 'P' */
+ { 0x3e, 0x41, 0x51, 0x21, 0x5e }, /* 'Q' */
+ { 0x7f, 0x09, 0x19, 0x29, 0x46 }, /* 'R' */
+ { 0x46, 0x49, 0x49, 0x49, 0x31 }, /* 'S' */
+ { 0x01, 0x01, 0x7f, 0x01, 0x01 }, /* 'T' */
+ { 0x3f, 0x40, 0x40, 0x40, 0x3f }, /* 'U' */
+ { 0x1f, 0x20, 0x40, 0x20, 0x1f }, /* 'V' */
+ { 0x7f, 0x20, 0x18, 0x20, 0x7f }, /* 'W' */
+ { 0x63, 0x14, 0x08, 0x14, 0x63 }, /* 'X' */
+ { 0x03, 0x04, 0x78, 0x04, 0x03 }, /* 'Y' */
+ { 0x61, 0x51, 0x49, 0x45, 0x43 }, /* 'Z' */
+};
+
+static const uint8_t g_glyph_x[NVDRO_FONT_W] =
+{
+ 0x44, 0x28, 0x10, 0x28, 0x44 /* 'x' */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static FAR const uint8_t *nvdro_glyph(char ch)
+{
+ if (ch == 'x')
+ {
+ return g_glyph_x;
+ }
+
+ if (ch >= 'a' && ch <= 'z')
+ {
+ ch -= 'a' - 'A';
+ }
+
+ if (ch < NVDRO_FONT_FIRST || ch > NVDRO_FONT_LAST)
+ {
+ ch = '?';
+ }
+
+ return g_font[ch - NVDRO_FONT_FIRST];
+}
+
+/* Copy w x h pixels from g_buf to the screen at (x, y), clipped */
+
+static void nvdro_blit(int x, int y, int w, int h)
+{
+ struct lcddev_area_s area;
+ int stride = w * sizeof(uint16_t);
+ int x0 = x;
+ int y0 = y;
+ int x1 = x + w - 1;
+ int y1 = y + h - 1;
+
+ if (g_lcdfd < 0 || w <= 0 || h <= 0)
+ {
+ return;
+ }
+
+ if (x0 < 0)
+ {
+ x0 = 0;
+ }
+
+ if (y0 < 0)
+ {
+ y0 = 0;
+ }
+
+ if (x1 >= g_xres)
+ {
+ x1 = g_xres - 1;
+ }
+
+ if (y1 >= g_yres)
+ {
+ y1 = g_yres - 1;
+ }
+
+ if (x0 > x1 || y0 > y1)
+ {
+ return;
+ }
+
+ area.row_start = y0;
+ area.row_end = y1;
+ area.col_start = x0;
+ area.col_end = x1;
+ area.stride = stride;
+ area.data = (FAR uint8_t *)&g_buf[(y0 - y) * w + (x0 - x)];
+
+ ioctl(g_lcdfd, LCDDEVIO_PUTAREA, (unsigned long)((uintptr_t)&area));
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int nvdro_lcd_open(FAR const char *path)
+{
+ struct fb_videoinfo_s vinfo;
+ int ret;
+
+ g_lcdfd = open(path, O_RDWR);
+ if (g_lcdfd < 0)
+ {
+ return -errno;
+ }
+
+ ret = ioctl(g_lcdfd, LCDDEVIO_GETVIDEOINFO,
+ (unsigned long)((uintptr_t)&vinfo));
+ if (ret < 0 || vinfo.fmt != FB_FMT_RGB16_565)
+ {
+ ret = ret < 0 ? -errno : -ENOTSUP;
+ nvdro_lcd_close();
+ return ret;
+ }
+
+ g_xres = vinfo.xres;
+ g_yres = vinfo.yres;
+
+ ioctl(g_lcdfd, LCDDEVIO_SETPOWER, CONFIG_LCD_MAXPOWER);
+ return 0;
+}
+
+void nvdro_lcd_close(void)
+{
+ if (g_lcdfd >= 0)
+ {
+ close(g_lcdfd);
+ g_lcdfd = -1;
+ }
+}
+
+int nvdro_lcd_width(void)
+{
+ return g_xres;
+}
+
+int nvdro_lcd_height(void)
+{
+ return g_yres;
+}
+
+void nvdro_fill(int x, int y, int w, int h, uint16_t color)
+{
+ int band;
+ int i;
+
+ if (w <= 0 || h <= 0)
+ {
+ return;
+ }
+
+ /* Paint in horizontal bands that fit in the scratch buffer */
+
+ band = NVDRO_BUFPIXELS / w;
+ if (band > h)
+ {
+ band = h;
+ }
+
+ for (i = 0; i < band * w; i++)
+ {
+ g_buf[i] = color;
+ }
+
+ while (h > 0)
+ {
+ int n = h < band ? h : band;
+
+ nvdro_blit(x, y, w, n);
+ y += n;
+ h -= n;
+ }
+}
+
+void nvdro_putrow(int x, int y, int w, FAR const uint16_t *pixels)
+{
+ if (w > NVDRO_BUFPIXELS)
+ {
+ w = NVDRO_BUFPIXELS;
+ }
+
+ memcpy(g_buf, pixels, w * sizeof(uint16_t));
+ nvdro_blit(x, y, w, 1);
+}
+
+void nvdro_frame(int x, int y, int w, int h, int t, uint16_t color)
+{
+ nvdro_fill(x, y, w, t, color);
+ nvdro_fill(x, y + h - t, w, t, color);
+ nvdro_fill(x, y + t, t, h - 2 * t, color);
+ nvdro_fill(x + w - t, y + t, t, h - 2 * t, color);
+}
+
+int nvdro_text_width(int nchars, int scale, int gap)
+{
+ return nchars * (NVDRO_FONT_W * scale + gap);
+}
+
+int nvdro_text(int x, int y, FAR const char *str, int scale, int gap,
+ uint16_t fg, uint16_t bg)
+{
+ int cw = NVDRO_FONT_W * scale + gap;
+ int ch = NVDRO_FONT_H * scale;
+ int x0 = x;
+
+ if (cw * ch > NVDRO_BUFPIXELS)
+ {
+ return 0;
+ }
+
+ for (; *str != '\0'; str++)
+ {
+ FAR const uint8_t *glyph = nvdro_glyph(*str);
+ int row;
+ int col;
+
+ for (row = 0; row < ch; row++)
+ {
+ FAR uint16_t *dst = &g_buf[row * cw];
+ int bit = 1 << (row / scale);
+
+ for (col = 0; col < cw; col++)
+ {
+ int gc = col / scale;
+
+ dst[col] = (gc < NVDRO_FONT_W && (glyph[gc] & bit)) ? fg : bg;
+ }
+ }
+
+ nvdro_blit(x, y, cw, ch);
+ x += cw;
+ }
+
+ return x - x0;
+}
diff --git a/industry/cnc/nvmpg-dro/nvmpg_dro_logo.c
b/industry/cnc/nvmpg-dro/nvmpg_dro_logo.c
new file mode 100644
index 000000000..e76176188
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/nvmpg_dro_logo.c
@@ -0,0 +1,707 @@
+/****************************************************************************
+ * apps/industry/cnc/nvmpg-dro/nvmpg_dro_logo.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* NuttX logo splash. The image data is the RGB565 run-length encoded logo
+ * from apps/examples/nximage/nximage_bitmap.c.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include "nvmpg_dro.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const uint16_t g_lut[] =
+{
+ 0x0000, 0x1105, 0x5cdc, 0x399c, 0x41bc, 0x0845, 0x397c, 0x39bc,
+ 0x41bd, 0x39ba, 0x28ee, 0x3132, 0x31f2, 0x5d1d, 0x42ba, 0x3152,
+ 0x399a, 0x6b8f, 0xd73e, 0x315c, 0x2945, 0x8c7d, 0xe7be, 0xdf7e,
+ 0x313c, 0xadd9, 0xb63e, 0x5abd, 0xdf9e, 0xdf9f, 0x4a52, 0x2950,
+ 0x292f, 0x3176, 0xcf1e, 0xdf5f, 0xadf9, 0x5bf3, 0x5cd8, 0x326c,
+ 0xe7df, 0x18af, 0x3153, 0xdf5e, 0xffff, 0x5497, 0x10e4, 0xdf7f,
+ 0x5cb8, 0x8c9d, 0x8437, 0x9e1b, 0xef9f, 0x9dfb, 0xb5fe, 0xffdf,
+ 0x41be, 0x53d6, 0x5436, 0x2910, 0x3156, 0x7539, 0x39bb, 0x7c16,
+ 0x95fb, 0x28ef, 0x653d, 0xe7bf, 0x188f, 0x397a, 0x3977, 0x20d1,
+ 0x8c7e, 0xe79f, 0x9e3b, 0x186e, 0x5416, 0x2930, 0x0801, 0x41de,
+ 0x313d, 0x3173, 0x7c36, 0x08e5, 0x549c, 0xd75f, 0x311c, 0x73d6,
+ 0x3150, 0x315d, 0x104e, 0x080e, 0x20f0, 0x2912, 0x54da, 0xdf3e,
+ 0x5cfa, 0x3a1c, 0x425c, 0xcefe, 0x4bd7, 0x843d, 0x2932, 0x3997,
+ 0xf7df, 0x397d, 0x2936, 0x20ef, 0x315a, 0x20f1, 0x0005, 0xd75e,
+ 0x53fa, 0x290f, 0x214b, 0x20cf, 0x4bf6, 0x2919, 0x3212, 0x9ebd,
+ 0x28fc, 0x5415, 0x399d, 0x73d5, 0x0804, 0x3187, 0x328d, 0x2a2b,
+ 0x7559, 0xe77f, 0xb61c, 0x3186, 0xa598, 0xbebd, 0x73d0, 0x84d5,
+ 0x5cb7, 0x2916, 0x20ce, 0x3a35, 0x53fc,
+};
+
+/* Run-length encoded image: (pixel count, g_lut index) pairs */
+
+static const uint8_t g_nuttx[] =
+{
+ 76, 0, 1, 1, 1, 2, 1, 3, 4, 4, 1, 5, 76, 0, 75, 0, 1, 1, 1, 2, 1, 6, 1, 7,
+ 2, 8, 3, 4, 1, 5, 75, 0, 74, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 10, 1, 11,
+ 1, 8, 3, 4, 1, 5, 74, 0, 73, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13,
+ 1, 14, 1, 15, 1, 8, 3, 4, 1, 5, 73, 0, 72, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9,
+ 1, 12, 1, 13, 1, 6, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 72, 0, 71, 0, 1,
+ 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 2, 4, 1, 7, 1, 16, 1, 15, 1,
+ 8, 3, 4, 1, 5, 71, 0, 70, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 4, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 70, 0, 69, 0, 1, 1, 1, 2, 1,
+ 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 6, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1,
+ 5, 69, 0, 68, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 8, 4, 1,
+ 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 68, 0, 67, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1,
+ 9, 1, 12, 1, 13, 1, 6, 10, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 67, 0,
+ 66, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 12, 4, 1, 7, 1,
+ 16, 1, 15, 1, 8, 3, 4, 1, 5, 66, 0, 65, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1,
+ 12, 1, 13, 1, 6, 14, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 65, 0, 64, 0,
+ 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 16, 4, 1, 7, 1, 16, 1,
+ 15, 1, 8, 3, 4, 1, 5, 64, 0, 63, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 6, 18, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 63, 0, 62, 0, 1, 1,
+ 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 20, 4, 1, 7, 1, 16, 1, 15, 1,
+ 8, 3, 4, 1, 5, 62, 0, 61, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 22, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 61, 0, 60, 0, 1, 1, 1, 2,
+ 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 24, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3,
+ 4, 1, 5, 60, 0, 59, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6,
+ 26, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 59, 0, 58, 0, 1, 1, 1, 2, 1,
+ 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 28, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4,
+ 1, 5, 26, 0, 1, 17, 5, 18, 1, 17, 25, 0, 57, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1,
+ 9, 1, 12, 1, 13, 1, 6, 10, 4, 5, 19, 15, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4,
+ 1, 5, 23, 0, 1, 20, 9, 18, 1, 20, 23, 0, 56, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1,
+ 9, 1, 12, 1, 13, 1, 6, 9, 4, 1, 19, 1, 21, 1, 22, 3, 23, 1, 22, 1, 21, 1,
+ 24, 14, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 22, 0, 11, 18, 1, 20, 22,
+ 0, 23, 0, 1, 17, 5, 18, 1, 25, 25, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12,
+ 1, 13, 1, 6, 9, 4, 1, 19, 1, 26, 7, 18, 1, 22, 1, 27, 14, 4, 1, 7, 1, 16,
+ 1, 15, 1, 8, 3, 4, 1, 5, 20, 0, 1, 17, 12, 18, 1, 20, 21, 0, 21, 0, 1, 20,
+ 9, 18, 1, 20, 22, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 9,
+ 4, 1, 19, 1, 26, 9, 18, 1, 28, 1, 27, 4, 8, 1, 7, 9, 4, 1, 7, 1, 16, 1, 15,
+ 1, 8, 3, 4, 1, 5, 19, 0, 14, 18, 21, 0, 21, 0, 11, 18, 1, 20, 20, 0, 1, 1,
+ 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 10, 4, 1, 21, 11, 18, 1, 29, 1,
+ 30, 2, 31, 1, 32, 1, 33, 1, 8, 9, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5,
+ 17, 0, 1, 17, 5, 18, 1, 34, 2, 35, 1, 34, 5, 18, 1, 17, 20, 0, 20, 0, 1,
+ 36, 12, 18, 1, 37, 4, 38, 1, 39, 13, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1,
+ 12, 1, 13, 1, 6, 10, 4, 1, 24, 1, 22, 4, 18, 3, 34, 5, 18, 1, 40, 1, 41, 3,
+ 31, 1, 42, 1, 8, 9, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 16, 0, 5, 18,
+ 1, 43, 4, 44, 1, 43, 4, 18, 1, 35, 1, 45, 1, 46, 18, 0, 19, 0, 1, 17, 13,
+ 18, 1, 47, 1, 45, 3, 38, 1, 48, 1, 38, 11, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9,
+ 1, 12, 1, 13, 1, 6, 11, 4, 1, 49, 4, 18, 1, 35, 3, 44, 1, 43, 5, 18, 1, 50,
+ 4, 31, 1, 42, 1, 8, 9, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 14, 0, 1,
+ 17, 4, 18, 1, 34, 6, 44, 1, 34, 4, 18, 1, 51, 1, 38, 1, 46, 17, 0, 19, 0,
+ 5, 18, 1, 34, 2, 52, 1, 35, 6, 18, 1, 53, 4, 38, 1, 48, 1, 39, 9, 0, 1, 1,
+ 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 11, 4, 1, 3, 1, 54, 4, 18, 1,
+ 55, 4, 44, 1, 43, 4, 18, 1, 40, 1, 41, 4, 31, 1, 42, 1, 8, 9, 4, 1, 7, 1,
+ 16, 1, 15, 1, 8, 3, 4, 1, 5, 13, 0, 5, 18, 1, 52, 6, 44, 1, 52, 4, 18, 1,
+ 53, 2, 38, 1, 46, 16, 0, 18, 0, 1, 17, 4, 18, 1, 43, 4, 44, 1, 35, 5, 18,
+ 1, 47, 1, 45, 5, 38, 8, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 12, 4, 1, 19, 1, 23, 3, 18, 1, 35, 6, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1,
+ 33, 1, 7, 9, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 11, 0, 1, 17, 4, 18,
+ 1, 34, 7, 44, 1, 52, 4, 18, 1, 53, 3, 38, 16, 0, 18, 0, 4, 18, 1, 34, 5,
+ 44, 1, 55, 1, 34, 5, 18, 1, 53, 4, 38, 1, 48, 1, 39, 6, 0, 1, 1, 1, 2, 1,
+ 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 13, 4, 1, 19, 1, 23, 3, 18, 1, 52, 6,
+ 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 10, 4, 1, 7, 1, 16, 1,
+ 15, 1, 8, 3, 4, 1, 5, 10, 0, 5, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 51, 2,
+ 38, 1, 48, 1, 39, 15, 0, 18, 0, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 47,
+ 1, 45, 5, 38, 5, 0, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 14,
+ 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1,
+ 7, 10, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 8, 0, 1, 17, 4, 18, 1, 34,
+ 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 4, 38, 15, 0, 18, 0, 3, 18, 1, 34, 8,
+ 44, 1, 34, 5, 18, 1, 53, 4, 38, 1, 48, 1, 39, 3, 0, 1, 1, 1, 2, 1, 6, 1, 7,
+ 1, 9, 1, 12, 1, 13, 1, 6, 15, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 52,
+ 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 11, 4, 1, 7, 1, 16, 1, 15, 1, 8,
+ 3, 4, 1, 5, 7, 0, 5, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 1,
+ 14, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 52, 5, 18, 1, 47, 1, 45, 5, 38, 2, 0,
+ 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 16, 4, 1, 19, 1, 23, 2,
+ 18, 1, 34, 9, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 7, 11, 4, 1, 7, 1,
+ 16, 1, 15, 1, 8, 3, 4, 1, 5, 5, 0, 1, 17, 4, 18, 1, 34, 7, 44, 1, 52, 4,
+ 18, 1, 47, 1, 45, 5, 38, 1, 39, 14, 0, 18, 0, 3, 18, 1, 34, 9, 44, 1, 34,
+ 5, 18, 1, 53, 4, 38, 1, 48, 1, 39, 1, 1, 1, 2, 1, 6, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 6, 17, 4, 1, 19, 1, 23, 2, 18, 1, 34, 9, 44, 1, 52, 4, 18, 1, 40, 1,
+ 41, 4, 31, 1, 32, 1, 56, 12, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 4, 0,
+ 5, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 6, 38, 1, 1, 14, 0, 18, 0, 3, 18,
+ 1, 34, 9, 44, 1, 52, 5, 18, 1, 47, 1, 45, 3, 38, 1, 48, 1, 57, 1, 13, 1, 6,
+ 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 18, 4, 1, 19, 1, 23, 2, 18, 1, 34, 10, 44,
+ 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 7, 12, 4, 1, 7, 1, 16, 1, 15, 1, 8,
+ 3, 4, 1, 5, 2, 0, 1, 17, 4, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45,
+ 6, 38, 15, 0, 18, 0, 3, 18, 1, 34, 10, 44, 1, 34, 5, 18, 1, 53, 2, 38, 1,
+ 48, 1, 58, 1, 59, 1, 60, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 19, 4, 1, 19, 1,
+ 23, 2, 18, 1, 34, 10, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56,
+ 13, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 5, 1, 0, 5, 18, 1, 52, 7, 44, 1,
+ 34, 4, 18, 1, 61, 5, 38, 1, 48, 1, 39, 15, 0, 18, 0, 3, 18, 1, 34, 10, 44,
+ 1, 52, 5, 18, 1, 47, 1, 45, 1, 48, 1, 58, 1, 59, 1, 31, 1, 32, 1, 62, 1,
+ 12, 1, 13, 1, 6, 20, 4, 1, 19, 1, 23, 2, 18, 1, 34, 11, 44, 1, 34, 4, 18,
+ 1, 50, 5, 31, 1, 33, 1, 7, 13, 4, 1, 7, 1, 16, 1, 15, 1, 8, 3, 4, 1, 63, 4,
+ 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 16, 0, 18, 0, 3, 18,
+ 1, 34, 11, 44, 1, 34, 5, 18, 1, 64, 1, 58, 1, 59, 3, 31, 1, 65, 1, 66, 1,
+ 6, 21, 4, 1, 19, 1, 23, 2, 18, 1, 34, 11, 44, 1, 52, 4, 18, 1, 40, 1, 41,
+ 4, 31, 1, 32, 1, 56, 14, 4, 1, 7, 1, 16, 1, 15, 1, 8, 1, 4, 1, 24, 1, 22,
+ 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 16, 0, 18,
+ 0, 3, 18, 1, 34, 11, 44, 1, 52, 5, 18, 1, 67, 1, 68, 5, 31, 1, 69, 22, 4,
+ 1, 19, 1, 23, 2, 18, 1, 34, 12, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1,
+ 7, 14, 4, 1, 7, 1, 16, 1, 15, 1, 8, 1, 21, 4, 18, 1, 34, 7, 44, 1, 52, 4,
+ 18, 1, 47, 1, 45, 6, 38, 17, 0, 18, 0, 3, 18, 1, 34, 12, 44, 1, 34, 5, 18,
+ 1, 50, 5, 31, 1, 70, 1, 7, 21, 4, 1, 19, 1, 23, 2, 18, 1, 34, 12, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 15, 4, 1, 7, 1, 16, 1, 71, 1,
+ 67, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 17, 0,
+ 18, 0, 3, 18, 1, 34, 12, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1,
+ 56, 21, 4, 1, 19, 1, 23, 2, 18, 1, 34, 13, 44, 1, 34, 4, 18, 1, 50, 5, 31,
+ 1, 33, 1, 7, 16, 4, 1, 72, 4, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45,
+ 6, 38, 18, 0, 18, 0, 3, 18, 1, 34, 13, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1,
+ 33, 1, 7, 20, 4, 1, 19, 1, 23, 2, 18, 1, 34, 13, 44, 1, 52, 4, 18, 1, 40,
+ 1, 41, 4, 31, 1, 32, 1, 56, 15, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44, 1,
+ 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 18, 0, 18, 0, 3, 18, 1, 34, 13, 44,
+ 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 20, 4, 1, 19, 1, 23, 2,
+ 18, 1, 34, 14, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 7, 14, 4, 1, 21,
+ 4, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 19, 0, 18, 0, 3,
+ 18, 1, 34, 14, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 19, 4, 1, 19,
+ 1, 23, 2, 18, 1, 34, 14, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1,
+ 56, 13, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38,
+ 1, 48, 1, 39, 19, 0, 18, 0, 3, 18, 1, 34, 14, 44, 1, 52, 5, 18, 1, 40, 1,
+ 41, 4, 31, 1, 32, 1, 56, 19, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 73,
+ 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 7, 12, 4, 1, 21, 4, 18, 1, 34,
+ 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 20, 0, 18, 0, 3, 18, 1, 34, 15,
+ 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 18, 4, 1, 19, 1, 23, 2, 18, 1,
+ 34, 7, 44, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56,
+ 11, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1,
+ 48, 1, 39, 20, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 73, 6, 44, 1, 52, 5, 18,
+ 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 18, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7,
+ 44, 1, 34, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 7, 10, 4, 1,
+ 21, 4, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 21, 0, 18, 0,
+ 3, 18, 1, 34, 8, 44, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7,
+ 17, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 2, 34, 7, 44, 1, 52, 4, 18, 1,
+ 40, 1, 41, 4, 31, 1, 32, 1, 56, 9, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44, 1,
+ 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 21, 0, 18, 0, 3, 18, 1, 34, 8, 44,
+ 1, 34, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 17,
+ 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 1, 18, 1, 52, 7, 44, 1, 34, 4,
+ 18, 1, 50, 5, 31, 1, 33, 1, 7, 8, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 52, 4,
+ 18, 1, 47, 1, 45, 6, 38, 22, 0, 18, 0, 3, 18, 1, 34, 8, 44, 2, 34, 7, 44,
+ 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 16, 4, 1, 19, 1, 23, 2, 18, 1, 34,
+ 7, 44, 1, 34, 1, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1,
+ 32, 1, 56, 7, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 74, 1,
+ 48, 4, 38, 1, 48, 1, 39, 22, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 1, 18,
+ 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 16, 4, 1,
+ 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 52, 7, 44, 1, 34, 4, 18,
+ 1, 50, 5, 31, 1, 33, 1, 7, 6, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 52, 4, 18,
+ 1, 40, 1, 75, 1, 58, 1, 48, 4, 38, 23, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1,
+ 34, 1, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 15, 4, 1,
+ 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 34, 7, 44, 1, 52, 4, 18,
+ 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 5, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44,
+ 1, 34, 4, 18, 1, 50, 1, 31, 1, 59, 1, 58, 1, 48, 1, 38, 1, 48, 1, 39, 23,
+ 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1,
+ 40, 1, 41, 4, 31, 1, 32, 1, 56, 15, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44,
+ 1, 34, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 7, 4, 4,
+ 1, 21, 4, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 2, 31, 1, 59, 1,
+ 58, 1, 48, 1, 38, 24, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 34,
+ 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 14, 4, 1, 19, 1, 23, 2, 18,
+ 1, 34, 7, 44, 1, 34, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4,
+ 31, 1, 32, 1, 56, 3, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1,
+ 50, 4, 31, 1, 59, 1, 76, 1, 39, 24, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34,
+ 3, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 14,
+ 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 4, 18, 1, 52, 7, 44, 1, 34, 4,
+ 18, 1, 50, 5, 31, 1, 33, 1, 7, 2, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 52, 4,
+ 18, 1, 40, 1, 41, 5, 31, 1, 77, 1, 78, 24, 0, 18, 0, 3, 18, 1, 34, 8, 44,
+ 1, 34, 3, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 13, 4,
+ 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 4, 18, 1, 34, 7, 44, 1, 52, 4,
+ 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 1, 4, 1, 24, 1, 22, 4, 18, 1, 52, 7,
+ 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 1, 5, 23, 0, 18, 0, 3, 18, 1,
+ 34, 8, 44, 1, 34, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31,
+ 1, 32, 1, 56, 13, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1,
+ 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 33, 1, 4, 1, 21, 4, 18, 1, 34, 7,
+ 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 79, 1, 7, 1, 4, 1, 5, 22, 0, 18,
+ 0, 3, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5,
+ 31, 1, 33, 1, 7, 12, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1,
+ 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 80, 1, 22, 4, 18,
+ 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 81, 1, 15, 1, 8, 2, 4, 1, 5,
+ 21, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 5, 18, 1, 52, 6, 44, 1, 52, 5,
+ 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 12, 4, 1, 19, 1, 23, 2, 18, 1, 34,
+ 7, 44, 1, 34, 6, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 82, 4,
+ 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 1, 16,
+ 1, 15, 1, 8, 2, 4, 1, 5, 20, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 5, 18,
+ 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 11, 4, 1, 19, 1, 23,
+ 2, 18, 1, 34, 7, 44, 1, 34, 6, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1,
+ 41, 3, 31, 1, 41, 1, 40, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 6, 31,
+ 1, 33, 2, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 19, 0, 17, 0, 1, 83, 3, 18, 1,
+ 34, 8, 44, 1, 34, 6, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31,
+ 1, 32, 1, 56, 11, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 7, 18, 1,
+ 52, 7, 44, 1, 34, 4, 18, 1, 50, 3, 31, 1, 50, 4, 18, 1, 34, 7, 44, 1, 52,
+ 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 2, 4, 1, 7, 1, 16, 1, 15, 1, 8,
+ 2, 4, 1, 5, 18, 0, 16, 0, 1, 1, 1, 84, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34,
+ 6, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 10, 4, 1, 19,
+ 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 3, 18, 1, 85, 3, 18, 1, 34, 7, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 1, 31, 1, 41, 1, 40, 4, 18, 1, 52, 7, 44, 1, 34,
+ 4, 18, 1, 50, 6, 31, 1, 33, 1, 7, 3, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1,
+ 5, 17, 0, 15, 0, 1, 1, 1, 2, 1, 86, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 7,
+ 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 10, 4,
+ 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 87, 4, 18, 1,
+ 52, 7, 44, 1, 34, 4, 18, 1, 50, 1, 88, 1, 50, 4, 18, 1, 34, 7, 44, 1, 52,
+ 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 5, 4, 1, 7, 1, 16, 1, 15, 1, 8,
+ 2, 4, 1, 5, 16, 0, 14, 0, 1, 1, 1, 2, 1, 6, 1, 89, 1, 23, 2, 18, 1, 34, 8,
+ 44, 1, 34, 7, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 9,
+ 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 90, 1, 40, 3,
+ 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 91, 1, 40, 4, 18, 1, 52, 7, 44,
+ 1, 34, 4, 18, 1, 50, 6, 31, 1, 33, 1, 7, 6, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2,
+ 4, 1, 5, 15, 0, 13, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 71, 1, 29, 2, 18, 1, 34,
+ 8, 44, 1, 34, 3, 18, 1, 85, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1,
+ 41, 4, 31, 1, 32, 1, 56, 9, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2,
+ 18, 1, 29, 1, 92, 1, 50, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 29, 4, 18,
+ 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 8, 4, 1, 7,
+ 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 14, 0, 12, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1,
+ 93, 1, 94, 1, 95, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 87, 4, 18,
+ 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 8, 4, 1, 19, 1, 23,
+ 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 41, 1, 40, 3, 18, 1,
+ 34, 7, 44, 1, 52, 9, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 33,
+ 1, 7, 9, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 13, 0, 11, 0, 1, 1, 1, 2,
+ 1, 6, 1, 8, 1, 93, 1, 96, 1, 97, 1, 28, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18,
+ 1, 29, 1, 90, 1, 40, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4,
+ 31, 1, 32, 1, 56, 8, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 1, 31, 1, 50, 4, 18, 1, 52, 7, 44, 1, 34, 7, 18, 1, 34, 7, 44,
+ 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 11, 4, 1, 7, 1, 16, 1, 15,
+ 1, 8, 2, 4, 1, 5, 12, 0, 10, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1,
+ 98, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 50,
+ 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 7, 4, 1, 19,
+ 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 31, 1, 41, 1,
+ 40, 3, 18, 1, 34, 7, 44, 1, 52, 7, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50,
+ 6, 31, 1, 33, 1, 7, 12, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 11, 0, 9,
+ 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 1, 4, 1, 19, 1, 23, 2, 18,
+ 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 41, 1, 40, 4, 18, 1, 52, 6,
+ 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 7, 4, 1, 19, 1, 23, 2,
+ 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 2, 31, 1, 50, 4, 18, 1, 52,
+ 7, 44, 1, 34, 5, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1,
+ 32, 1, 56, 14, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 10, 0, 8, 0, 1, 1,
+ 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 2, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8,
+ 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 31, 1, 50, 4, 18, 1, 34, 7, 44, 1, 34,
+ 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 6, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44,
+ 1, 34, 2, 18, 1, 29, 1, 92, 2, 31, 1, 41, 1, 40, 3, 18, 1, 34, 7, 44, 1,
+ 52, 5, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 33, 1, 7, 15, 4, 1,
+ 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 9, 0, 7, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1,
+ 93, 1, 96, 1, 98, 3, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 1, 31, 1, 41, 1, 40, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40,
+ 1, 41, 4, 31, 1, 32, 1, 56, 6, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34,
+ 2, 18, 1, 29, 1, 92, 3, 31, 1, 50, 4, 18, 1, 52, 7, 44, 1, 34, 3, 18, 1,
+ 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 17, 4, 1, 7, 1,
+ 16, 1, 15, 1, 8, 2, 4, 1, 5, 8, 0, 6, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1,
+ 96, 1, 98, 4, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1,
+ 92, 2, 31, 1, 50, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33,
+ 1, 7, 5, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92,
+ 3, 31, 1, 41, 1, 40, 3, 18, 1, 34, 7, 44, 1, 52, 3, 18, 1, 52, 7, 44, 1,
+ 34, 4, 18, 1, 50, 6, 31, 1, 33, 1, 7, 18, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2,
+ 4, 1, 5, 7, 0, 5, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 5, 4, 1,
+ 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 2, 31, 1, 41,
+ 1, 40, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1,
+ 56, 5, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 4,
+ 31, 1, 50, 4, 18, 1, 52, 7, 44, 1, 34, 1, 18, 1, 34, 7, 44, 1, 52, 4, 18,
+ 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 20, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4,
+ 1, 5, 6, 0, 4, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 6, 4, 1, 19,
+ 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 3, 31, 1, 50, 4,
+ 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 4, 4, 1, 19, 1,
+ 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 4, 31, 1, 41, 1, 40,
+ 3, 18, 1, 34, 7, 44, 1, 52, 1, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 6,
+ 31, 1, 33, 1, 7, 21, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 5, 0, 3, 0,
+ 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 7, 4, 1, 19, 1, 23, 2, 18, 1,
+ 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 3, 31, 1, 41, 1, 40, 4, 18, 1, 52,
+ 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 4, 4, 1, 19, 1, 23,
+ 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 5, 31, 1, 50, 4, 18, 1,
+ 52, 7, 44, 1, 99, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56,
+ 23, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 4, 0, 2, 0, 1, 1, 1, 2, 1, 6,
+ 1, 8, 1, 93, 1, 96, 1, 98, 8, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34,
+ 2, 18, 1, 29, 1, 92, 4, 31, 1, 50, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1,
+ 50, 5, 31, 1, 33, 1, 7, 3, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2,
+ 18, 1, 29, 1, 92, 5, 31, 1, 41, 1, 67, 3, 18, 1, 34, 15, 44, 1, 34, 4, 18,
+ 1, 50, 6, 31, 1, 33, 1, 7, 24, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 3,
+ 0, 1, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 9, 4, 1, 19, 1, 23,
+ 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 4, 31, 1, 41, 1, 40, 4,
+ 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 3, 4, 1,
+ 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 72,
+ 4, 18, 1, 52, 13, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 26,
+ 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 2, 0, 1, 1, 1, 2, 1, 6, 1, 8, 1,
+ 93, 1, 96, 1, 98, 10, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18,
+ 1, 29, 1, 92, 5, 31, 1, 50, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5,
+ 31, 1, 33, 1, 7, 2, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 6, 31, 1, 80, 1, 22, 3, 18, 1, 34, 13, 44, 1, 34, 4, 18, 1, 50,
+ 6, 31, 1, 33, 1, 7, 27, 4, 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 5, 1, 0, 1,
+ 2, 1, 6, 1, 8, 1, 93, 1, 96, 1, 98, 11, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8,
+ 44, 1, 34, 2, 18, 1, 29, 1, 92, 5, 31, 1, 41, 1, 67, 4, 18, 1, 52, 6, 44,
+ 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56, 2, 4, 1, 19, 1, 23, 2, 18,
+ 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 1, 21, 4, 18, 1, 52,
+ 11, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 32, 1, 56, 29, 4, 1, 7, 1,
+ 16, 1, 15, 1, 8, 2, 4, 1, 5, 1, 3, 1, 8, 1, 93, 1, 96, 1, 98, 12, 4, 1, 19,
+ 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 72, 4,
+ 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 33, 1, 7, 1, 4, 1, 19, 1,
+ 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 1, 24, 1,
+ 22, 3, 18, 1, 34, 11, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 33, 1, 7, 30, 4,
+ 1, 7, 1, 16, 1, 15, 1, 8, 2, 4, 1, 4, 1, 33, 1, 100, 1, 98, 13, 4, 1, 19,
+ 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 80, 1,
+ 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 56,
+ 1, 4, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31,
+ 1, 8, 1, 4, 1, 101, 4, 18, 1, 52, 9, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31,
+ 1, 32, 1, 56, 32, 4, 1, 7, 1, 16, 1, 15, 1, 56, 1, 7, 1, 4, 1, 8, 1, 102,
+ 1, 9, 1, 7, 12, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29,
+ 1, 92, 6, 31, 1, 8, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31,
+ 1, 33, 1, 7, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92,
+ 6, 31, 1, 8, 1, 4, 1, 101, 4, 18, 1, 34, 9, 44, 1, 35, 4, 18, 1, 50, 6, 31,
+ 1, 33, 1, 7, 33, 4, 1, 7, 1, 103, 1, 10, 1, 8, 2, 4, 1, 8, 1, 15, 1, 16, 1,
+ 7, 11, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6,
+ 31, 1, 8, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 4,
+ 31, 1, 32, 1, 56, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29,
+ 1, 92, 6, 31, 1, 8, 1, 24, 1, 22, 4, 18, 1, 52, 8, 44, 1, 104, 4, 18, 1,
+ 29, 1, 68, 6, 31, 1, 56, 33, 4, 1, 7, 1, 9, 1, 12, 1, 66, 1, 3, 3, 4, 1, 8,
+ 1, 15, 1, 16, 1, 7, 10, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18,
+ 1, 29, 1, 92, 6, 31, 1, 8, 1, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18,
+ 1, 50, 5, 31, 1, 33, 1, 19, 1, 23, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 6, 31, 1, 8, 1, 21, 4, 18, 1, 34, 10, 44, 1, 34, 4, 18, 1, 50,
+ 6, 31, 1, 56, 32, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 3, 4, 1,
+ 8, 1, 15, 1, 16, 1, 7, 9, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2,
+ 18, 1, 29, 1, 92, 6, 31, 1, 8, 1, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1,
+ 52, 5, 18, 1, 40, 1, 41, 4, 31, 1, 32, 1, 105, 1, 23, 2, 18, 1, 34, 7, 44,
+ 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 80, 1, 22, 4, 18, 1, 52, 10, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 1, 7, 30, 4, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 6, 1, 4, 1, 5, 1, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 8, 4, 1,
+ 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 2,
+ 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 5, 31, 1, 106, 1, 23,
+ 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 72, 4, 18, 1,
+ 34, 12, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1, 56, 29, 4, 1, 7, 1, 9, 1,
+ 12, 1, 13, 1, 6, 1, 4, 1, 5, 1, 0, 2, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1,
+ 7, 7, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6,
+ 31, 1, 8, 2, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1,
+ 41, 4, 31, 1, 107, 1, 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92,
+ 5, 31, 1, 41, 1, 67, 4, 18, 1, 52, 12, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5,
+ 31, 1, 33, 1, 7, 27, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 2, 0,
+ 3, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 6, 4, 1, 19, 1, 23, 2, 18, 1,
+ 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 3, 4, 1, 21, 4, 18, 1,
+ 34, 7, 44, 1, 34, 5, 18, 1, 50, 4, 31, 1, 92, 1, 29, 2, 18, 1, 34, 7, 44,
+ 1, 34, 2, 18, 1, 29, 1, 92, 5, 31, 1, 50, 4, 18, 1, 34, 14, 44, 1, 34, 4,
+ 18, 1, 50, 5, 31, 1, 32, 1, 56, 26, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1,
+ 4, 1, 5, 3, 0, 4, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 5, 4, 1, 19, 1,
+ 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 3, 4, 1,
+ 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 3, 31, 1, 92,
+ 1, 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 4, 31, 1, 41, 1,
+ 40, 4, 18, 1, 52, 14, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 1, 7,
+ 24, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 4, 0, 5, 0, 1, 5, 3, 4,
+ 1, 8, 1, 15, 1, 16, 1, 7, 4, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34,
+ 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 4, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34,
+ 5, 18, 1, 50, 3, 31, 1, 92, 1, 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 4, 31, 1, 50, 4, 18, 1, 34, 16, 44, 1, 34, 4, 18, 1, 50, 5, 31,
+ 1, 32, 1, 56, 23, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 5, 0, 6,
+ 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 3, 4, 1, 19, 1, 23, 2, 18, 1, 34,
+ 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 4, 4, 1, 24, 1, 22, 4, 18,
+ 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 2, 31, 1, 92, 1, 29, 2, 18, 1,
+ 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 3, 31, 1, 41, 1, 40, 4, 18, 1, 52,
+ 7, 44, 1, 52, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 1, 7,
+ 21, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 6, 0, 7, 0, 1, 5, 3, 4,
+ 1, 8, 1, 15, 1, 16, 1, 7, 2, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34,
+ 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 5, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34,
+ 5, 18, 1, 50, 2, 31, 1, 92, 1, 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 3, 31, 1, 50, 4, 18, 1, 34, 8, 44, 1, 34, 1, 18, 1, 52, 7, 44,
+ 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1, 56, 20, 4, 1, 7, 1, 9, 1, 12, 1, 13,
+ 1, 6, 1, 4, 1, 5, 7, 0, 8, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 1, 4,
+ 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8,
+ 5, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 41, 1, 31,
+ 1, 92, 1, 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 2, 31, 1,
+ 41, 1, 40, 4, 18, 1, 52, 7, 44, 1, 52, 2, 18, 1, 34, 7, 44, 1, 52, 4, 18,
+ 1, 40, 1, 41, 5, 31, 1, 33, 1, 7, 18, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1,
+ 4, 1, 5, 8, 0, 9, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 1, 19, 1, 23, 2,
+ 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 6, 4, 1, 21, 4,
+ 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 1, 31, 1, 92, 1, 29, 2, 18, 1, 34,
+ 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 2, 31, 1, 50, 4, 18, 1, 34, 8, 44, 1,
+ 34, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1, 56, 17, 4,
+ 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 9, 0, 10, 0, 1, 5, 3, 4, 1, 8,
+ 1, 15, 1, 16, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1,
+ 92, 6, 31, 1, 8, 6, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1,
+ 40, 1, 41, 1, 92, 1, 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92,
+ 1, 31, 1, 41, 1, 40, 4, 18, 1, 52, 7, 44, 1, 52, 4, 18, 1, 34, 7, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 1, 7, 15, 4, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 6, 1, 4, 1, 5, 10, 0, 11, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 108, 1, 23,
+ 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 7, 4, 1, 21,
+ 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 50, 1, 92, 1, 29, 2, 18, 1, 34, 7,
+ 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 31, 1, 50, 4, 18, 1, 34, 8, 44, 1, 34,
+ 5, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1, 56, 14, 4, 1, 7,
+ 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 11, 0, 12, 0, 1, 5, 3, 4, 1, 8, 1,
+ 109, 1, 29, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8,
+ 7, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 40, 1, 90, 1, 29,
+ 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 41, 1, 40, 4, 18, 1,
+ 52, 7, 44, 1, 52, 6, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31,
+ 1, 33, 1, 7, 12, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 12, 0, 13,
+ 0, 1, 5, 3, 4, 1, 89, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1,
+ 92, 6, 31, 1, 8, 8, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 87, 1,
+ 29, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 92, 1, 50, 4, 18, 1, 34,
+ 8, 44, 1, 34, 7, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1,
+ 56, 11, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 13, 0, 14, 0, 1, 5,
+ 2, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31,
+ 1, 8, 8, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5, 18, 1, 85, 3, 18,
+ 1, 34, 7, 44, 1, 34, 2, 18, 1, 29, 1, 90, 1, 40, 4, 18, 1, 52, 7, 44, 1,
+ 52, 4, 18, 1, 85, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31,
+ 1, 33, 1, 7, 9, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 14, 0, 15,
+ 0, 1, 5, 1, 4, 1, 19, 1, 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1,
+ 92, 6, 31, 1, 8, 9, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 8, 18, 1, 34, 7,
+ 44, 1, 34, 2, 18, 1, 29, 1, 87, 4, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 87,
+ 1, 40, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1, 56, 8, 4,
+ 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 15, 0, 16, 0, 1, 5, 1, 19, 1,
+ 23, 2, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 9, 4, 1,
+ 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 8, 18, 1, 34, 7, 44, 1, 34, 3, 18,
+ 1, 85, 4, 18, 1, 52, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 1, 50, 3, 18, 1,
+ 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 1, 7, 6, 4, 1, 7, 1,
+ 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 16, 0, 17, 0, 1, 110, 1, 111, 2, 18, 1,
+ 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 10, 4, 1, 21, 4, 18, 1,
+ 34, 7, 44, 1, 34, 7, 18, 1, 34, 7, 44, 1, 34, 7, 18, 1, 34, 8, 44, 1, 34,
+ 4, 18, 1, 50, 1, 31, 1, 41, 1, 40, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1,
+ 50, 5, 31, 1, 32, 1, 56, 5, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5,
+ 17, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8,
+ 10, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 7, 18, 1, 34, 7, 44, 1,
+ 34, 7, 18, 1, 52, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 2, 31, 1, 50, 3, 18,
+ 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 1, 7, 3, 4, 1, 7,
+ 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 18, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1,
+ 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 11, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1,
+ 34, 6, 18, 1, 34, 7, 44, 1, 34, 6, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50,
+ 3, 31, 1, 41, 1, 40, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1,
+ 32, 1, 56, 2, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 19, 0, 18, 0,
+ 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 11, 4, 1, 24,
+ 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 6, 18, 1, 34, 7, 44, 1, 34, 6, 18, 1,
+ 52, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 4, 31, 1, 50, 3, 18, 1, 34, 7, 44,
+ 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1, 33, 2, 7, 1, 9, 1, 12, 1, 13, 1, 6,
+ 1, 4, 1, 5, 20, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92,
+ 6, 31, 1, 8, 12, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 34, 7, 44,
+ 1, 34, 5, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 41, 1, 40, 3,
+ 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5, 31, 1, 32, 1, 56, 1, 9, 1, 12, 1,
+ 13, 1, 6, 1, 4, 1, 5, 21, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1,
+ 29, 1, 92, 6, 31, 1, 8, 12, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 5,
+ 18, 1, 34, 7, 44, 1, 34, 5, 18, 1, 52, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41,
+ 6, 31, 1, 82, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 5, 31, 1,
+ 81, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 22, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1,
+ 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 13, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1,
+ 34, 4, 18, 1, 34, 7, 44, 1, 34, 4, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50,
+ 6, 31, 1, 32, 1, 80, 1, 22, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 50, 5,
+ 31, 1, 77, 1, 112, 1, 6, 1, 4, 1, 5, 23, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1,
+ 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 13, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6,
+ 44, 1, 52, 4, 18, 1, 34, 7, 44, 1, 34, 4, 18, 1, 52, 7, 44, 1, 52, 4, 18,
+ 1, 40, 1, 41, 6, 31, 1, 33, 1, 4, 1, 21, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18,
+ 1, 40, 1, 41, 5, 31, 1, 113, 1, 8, 1, 5, 24, 0, 18, 0, 3, 18, 1, 34, 8, 44,
+ 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 14, 4, 1, 21, 4, 18, 1, 34, 7, 44,
+ 1, 34, 3, 18, 1, 34, 7, 44, 1, 34, 3, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1,
+ 50, 6, 31, 1, 32, 1, 56, 1, 4, 1, 24, 1, 22, 3, 18, 1, 52, 7, 44, 1, 34, 4,
+ 18, 1, 50, 4, 31, 1, 59, 1, 58, 1, 114, 25, 0, 18, 0, 3, 18, 1, 34, 8, 44,
+ 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 14, 4, 1, 24, 1, 22, 4, 18, 1, 52,
+ 6, 44, 1, 52, 3, 18, 1, 34, 7, 44, 1, 34, 3, 18, 1, 52, 7, 44, 1, 52, 4,
+ 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7, 2, 4, 1, 21, 3, 18, 1, 34, 7, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 2, 31, 1, 59, 1, 58, 1, 48, 1, 38, 25, 0, 18, 0,
+ 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 15, 4, 1, 21,
+ 4, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 34, 7, 44, 1, 34, 2, 18, 1, 34, 8,
+ 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 3, 4, 1, 24, 1, 22, 3, 18, 1,
+ 52, 7, 44, 1, 34, 4, 18, 1, 50, 1, 31, 1, 59, 1, 58, 1, 48, 1, 38, 1, 48,
+ 1, 39, 24, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6,
+ 31, 1, 8, 15, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 2, 18, 1, 34, 7,
+ 44, 1, 34, 2, 18, 1, 52, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33,
+ 1, 7, 4, 4, 1, 21, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 40, 1, 75, 1, 58,
+ 1, 48, 4, 38, 24, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1,
+ 92, 6, 31, 1, 8, 16, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 1, 18, 1, 34, 7,
+ 44, 1, 34, 1, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56,
+ 5, 4, 1, 24, 1, 22, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 74, 1, 48, 4, 38,
+ 1, 48, 1, 39, 23, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1,
+ 92, 6, 31, 1, 8, 16, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 1, 18, 1,
+ 34, 7, 44, 1, 34, 1, 18, 1, 52, 7, 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31,
+ 1, 33, 1, 7, 6, 4, 1, 21, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45,
+ 6, 38, 23, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6,
+ 31, 1, 8, 17, 4, 1, 21, 4, 18, 1, 34, 7, 44, 2, 34, 7, 44, 2, 34, 8, 44, 1,
+ 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 7, 4, 1, 24, 1, 22, 3, 18, 1, 52, 7,
+ 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 22, 0, 18, 0, 3, 18, 1, 34,
+ 8, 44, 1, 34, 2, 18, 1, 29, 1, 92, 6, 31, 1, 8, 17, 4, 1, 24, 1, 22, 4, 18,
+ 1, 52, 6, 44, 1, 52, 1, 34, 7, 44, 1, 34, 1, 52, 7, 44, 1, 52, 4, 18, 1,
+ 40, 1, 41, 6, 31, 1, 33, 1, 7, 8, 4, 1, 21, 3, 18, 1, 34, 7, 44, 1, 52, 4,
+ 18, 1, 47, 1, 45, 6, 38, 22, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18,
+ 1, 29, 1, 115, 6, 31, 1, 8, 18, 4, 1, 21, 4, 18, 1, 34, 7, 44, 1, 34, 7,
+ 44, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 9, 4, 1, 24, 1,
+ 22, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 21, 0,
+ 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 116, 1, 59, 5, 31, 1,
+ 8, 18, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 73, 7, 44, 1, 73, 7, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7, 10, 4, 1, 21, 3, 18, 1, 34, 7,
+ 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 21, 0, 18, 0, 3, 18, 1, 34, 8, 44,
+ 1, 34, 2, 18, 1, 35, 1, 45, 1, 58, 1, 59, 4, 31, 1, 16, 1, 7, 18, 4, 1, 21,
+ 4, 18, 1, 34, 22, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 11, 4, 1,
+ 24, 1, 22, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39,
+ 20, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 1, 48, 1,
+ 58, 1, 59, 3, 31, 1, 42, 1, 16, 1, 7, 17, 4, 1, 24, 1, 22, 4, 18, 1, 52,
+ 20, 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7, 12, 4, 1, 21, 3,
+ 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 20, 0, 18, 0, 3, 18,
+ 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 1, 38, 1, 48, 1, 58, 1, 59, 2,
+ 31, 1, 56, 1, 15, 1, 16, 1, 7, 17, 4, 1, 21, 4, 18, 1, 34, 20, 44, 1, 34,
+ 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 12, 4, 1, 7, 1, 117, 1, 73, 3, 18, 1,
+ 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 19, 0, 18, 0, 3, 18,
+ 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 2, 38, 1, 48, 1, 58, 1, 59, 1,
+ 31, 2, 8, 1, 15, 1, 16, 1, 7, 16, 4, 1, 24, 1, 22, 4, 18, 1, 52, 18, 44, 1,
+ 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7, 11, 4, 1, 7, 1, 9, 1, 118, 1,
+ 119, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 19, 0, 18, 0,
+ 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 3, 38, 1, 48, 1, 58, 1,
+ 113, 1, 8, 1, 4, 1, 8, 1, 15, 1, 16, 1, 7, 16, 4, 1, 21, 4, 18, 1, 34, 18,
+ 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 11, 4, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 120, 1, 22, 3, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48,
+ 1, 39, 18, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 4,
+ 38, 1, 48, 1, 121, 1, 122, 2, 4, 1, 8, 1, 15, 1, 16, 1, 7, 15, 4, 1, 24, 1,
+ 22, 4, 18, 1, 52, 16, 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7,
+ 10, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 123, 3, 18, 1, 34, 7, 44,
+ 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 18, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1,
+ 34, 2, 18, 1, 35, 1, 45, 5, 38, 1, 48, 1, 124, 3, 4, 1, 8, 1, 15, 1, 16, 1,
+ 7, 15, 4, 1, 21, 4, 18, 1, 34, 16, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32,
+ 1, 56, 10, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 1, 125, 4, 18, 1,
+ 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 17, 0, 18, 0, 3, 18,
+ 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 1, 0, 1, 5, 3, 4, 1, 8, 1,
+ 15, 1, 16, 1, 7, 14, 4, 1, 24, 1, 22, 4, 18, 1, 52, 14, 44, 1, 52, 4, 18,
+ 1, 40, 1, 41, 6, 31, 1, 33, 1, 7, 9, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1,
+ 4, 1, 5, 2, 0, 1, 17, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6,
+ 38, 17, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38,
+ 2, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 14, 4, 1, 21, 4, 18, 1, 34, 14,
+ 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 9, 4, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 6, 1, 4, 1, 5, 4, 0, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5,
+ 38, 1, 48, 1, 39, 16, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35,
+ 1, 45, 6, 38, 3, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 13, 4, 1, 24, 1,
+ 22, 4, 18, 1, 52, 12, 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7,
+ 8, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 5, 0, 1, 17, 3, 18, 1,
+ 34, 7, 44, 1, 52, 4, 18, 1, 47, 1, 45, 6, 38, 16, 0, 18, 0, 3, 18, 1, 34,
+ 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 4, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1,
+ 16, 1, 7, 13, 4, 1, 21, 4, 18, 1, 34, 12, 44, 1, 34, 4, 18, 1, 50, 6, 31,
+ 1, 32, 1, 56, 8, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 7, 0, 4,
+ 18, 1, 52, 7, 44, 1, 34, 4, 18, 1, 53, 5, 38, 1, 48, 1, 39, 15, 0, 18, 0,
+ 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 5, 0, 1, 5, 3, 4,
+ 1, 8, 1, 15, 1, 16, 1, 7, 12, 4, 1, 24, 1, 22, 4, 18, 1, 52, 10, 44, 1, 52,
+ 4, 18, 1, 40, 1, 41, 6, 31, 1, 33, 1, 7, 7, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 1, 4, 1, 5, 8, 0, 1, 17, 3, 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 47, 1,
+ 45, 6, 38, 15, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45,
+ 6, 38, 6, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 12, 4, 1, 21, 4, 18, 1,
+ 34, 10, 44, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 7, 4, 1, 7, 1, 9, 1,
+ 12, 1, 13, 1, 6, 1, 4, 1, 5, 10, 0, 4, 18, 1, 52, 7, 44, 1, 34, 4, 18, 1,
+ 51, 5, 38, 1, 48, 1, 126, 14, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18,
+ 1, 35, 1, 45, 6, 38, 7, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 11, 4, 1,
+ 24, 1, 22, 4, 18, 1, 52, 8, 44, 1, 52, 4, 18, 1, 40, 1, 41, 6, 31, 1, 33,
+ 1, 7, 6, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 11, 0, 1, 17, 3,
+ 18, 1, 34, 7, 44, 1, 52, 4, 18, 1, 53, 6, 38, 1, 127, 14, 0, 18, 0, 3, 18,
+ 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 8, 0, 1, 5, 3, 4, 1, 8, 1,
+ 15, 1, 16, 1, 7, 11, 4, 1, 21, 4, 18, 1, 34, 8, 44, 1, 34, 4, 18, 1, 50, 6,
+ 31, 1, 32, 1, 56, 6, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 13, 0,
+ 4, 18, 1, 52, 6, 44, 1, 52, 4, 18, 1, 53, 7, 38, 14, 0, 18, 0, 3, 18, 1,
+ 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 9, 0, 1, 5, 3, 4, 1, 8, 1,
+ 15, 1, 16, 1, 7, 10, 4, 1, 24, 1, 22, 4, 18, 1, 52, 6, 44, 1, 52, 4, 18, 1,
+ 40, 1, 41, 6, 31, 1, 33, 1, 7, 5, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4,
+ 1, 5, 14, 0, 1, 17, 3, 18, 1, 34, 6, 44, 1, 52, 4, 18, 1, 128, 6, 38, 1,
+ 48, 1, 126, 13, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45,
+ 6, 38, 10, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 10, 4, 1, 21, 4, 18, 1,
+ 34, 1, 55, 4, 44, 1, 55, 1, 34, 4, 18, 1, 50, 6, 31, 1, 32, 1, 56, 5, 4, 1,
+ 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 16, 0, 4, 18, 1, 52, 5, 44, 1, 34,
+ 3, 18, 1, 47, 1, 45, 7, 38, 1, 39, 13, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1,
+ 34, 2, 18, 1, 35, 1, 45, 6, 38, 11, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1,
+ 7, 9, 4, 1, 24, 1, 22, 4, 18, 1, 34, 1, 52, 2, 44, 1, 52, 1, 34, 4, 18, 1,
+ 40, 1, 41, 6, 31, 1, 33, 1, 7, 4, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4,
+ 1, 5, 17, 0, 1, 17, 3, 18, 1, 34, 1, 52, 2, 44, 1, 52, 1, 34, 4, 18, 1, 53,
+ 8, 38, 1, 39, 13, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1,
+ 45, 6, 38, 12, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 9, 4, 1, 21, 6, 18,
+ 2, 34, 6, 18, 1, 50, 6, 31, 1, 32, 1, 56, 4, 4, 1, 7, 1, 9, 1, 12, 1, 13,
+ 1, 6, 1, 4, 1, 5, 19, 0, 5, 18, 2, 34, 5, 18, 1, 47, 1, 45, 8, 38, 1, 1,
+ 13, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 13,
+ 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 8, 4, 1, 24, 1, 22, 12, 18, 1, 40,
+ 1, 41, 6, 31, 1, 33, 1, 7, 3, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1,
+ 5, 20, 0, 1, 17, 10, 18, 1, 35, 1, 61, 9, 38, 14, 0, 18, 0, 3, 18, 1, 34,
+ 8, 44, 1, 34, 2, 18, 1, 35, 1, 45, 6, 38, 14, 0, 1, 5, 3, 4, 1, 8, 1, 15,
+ 1, 16, 1, 7, 8, 4, 1, 21, 12, 18, 1, 50, 6, 31, 1, 32, 1, 56, 3, 4, 1, 7,
+ 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 22, 0, 9, 18, 1, 35, 1, 61, 8, 38, 1,
+ 48, 1, 39, 14, 0, 18, 0, 3, 18, 1, 34, 8, 44, 1, 18, 2, 35, 1, 129, 1, 45,
+ 6, 38, 15, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 7, 4, 1, 19, 1, 26, 10,
+ 18, 1, 130, 1, 107, 6, 31, 1, 33, 1, 7, 2, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 1, 4, 1, 5, 23, 0, 1, 131, 1, 132, 5, 18, 1, 35, 1, 133, 1, 128, 10, 38,
+ 15, 0, 25, 0, 9, 45, 7, 38, 16, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 7,
+ 4, 1, 19, 1, 21, 1, 22, 6, 18, 1, 40, 1, 50, 1, 107, 6, 31, 1, 32, 1, 56,
+ 2, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 27, 0, 1, 134, 1, 17, 1,
+ 135, 1, 51, 1, 45, 1, 136, 9, 38, 1, 48, 1, 39, 15, 0, 25, 0, 16, 38, 17,
+ 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 8, 4, 1, 24, 1, 21, 1, 22, 2, 29,
+ 1, 67, 1, 50, 1, 41, 8, 31, 1, 16, 1, 7, 1, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 1, 4, 1, 5, 30, 0, 1, 1, 11, 38, 1, 48, 1, 38, 16, 0, 25, 0, 16, 38, 18,
+ 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 9, 4, 1, 89, 1, 137, 1, 138, 1,
+ 92, 8, 31, 1, 42, 1, 16, 1, 7, 1, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4,
+ 1, 5, 32, 0, 1, 39, 1, 48, 8, 38, 1, 48, 1, 38, 17, 0, 25, 0, 16, 38, 19,
+ 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 9, 4, 1, 7, 1, 56, 1, 33, 1, 32,
+ 5, 31, 1, 42, 1, 16, 1, 8, 2, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1,
+ 5, 34, 0, 8, 38, 1, 48, 1, 38, 18, 0, 25, 0, 16, 38, 20, 0, 1, 5, 3, 4, 1,
+ 8, 1, 15, 1, 16, 1, 7, 10, 4, 1, 7, 7, 8, 3, 4, 1, 7, 1, 9, 1, 12, 1, 13,
+ 1, 6, 1, 4, 1, 5, 35, 0, 1, 46, 7, 38, 1, 39, 19, 0, 25, 0, 16, 38, 21, 0,
+ 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 19, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1,
+ 6, 1, 4, 1, 5, 64, 0, 25, 0, 16, 38, 22, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16,
+ 1, 7, 17, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 65, 0, 64, 0, 1,
+ 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 15, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6,
+ 1, 4, 1, 5, 66, 0, 65, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 13, 4, 1,
+ 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 67, 0, 66, 0, 1, 5, 3, 4, 1, 8, 1,
+ 15, 1, 16, 1, 7, 11, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 68, 0,
+ 67, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 9, 4, 1, 7, 1, 9, 1, 12, 1,
+ 13, 1, 6, 1, 4, 1, 5, 69, 0, 68, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7,
+ 7, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 70, 0, 69, 0, 1, 5, 3, 4,
+ 1, 8, 1, 15, 1, 16, 1, 7, 5, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5,
+ 71, 0, 70, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 3, 4, 1, 7, 1, 9, 1,
+ 12, 1, 13, 1, 6, 1, 4, 1, 5, 72, 0, 71, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 16,
+ 1, 7, 1, 4, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 73, 0, 72, 0, 1, 5,
+ 3, 4, 1, 8, 1, 15, 1, 16, 1, 7, 1, 9, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5, 74,
+ 0, 73, 0, 1, 5, 3, 4, 1, 8, 1, 15, 1, 103, 1, 12, 1, 13, 1, 6, 1, 4, 1, 5,
+ 75, 0, 74, 0, 1, 5, 3, 4, 1, 8, 1, 139, 1, 13, 1, 6, 1, 4, 1, 5, 76, 0, 75,
+ 0, 1, 5, 2, 4, 1, 3, 1, 140, 1, 6, 1, 4, 1, 5, 77, 0, 76, 0, 1, 5, 4, 4, 1,
+ 5, 78, 0,
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nvdro_logo
+ *
+ * Description:
+ * Clear the screen to the logo background and draw the logo centered.
+ *
+ ****************************************************************************/
+
+void nvdro_logo(void)
+{
+ FAR const uint8_t *pos = g_nuttx;
+ uint16_t row[NVDRO_LOGO_SIZE];
+ int x0 = (nvdro_lcd_width() - NVDRO_LOGO_SIZE) / 2;
+ int y0 = (nvdro_lcd_height() - NVDRO_LOGO_SIZE) / 2;
+ int y;
+
+ nvdro_fill(0, 0, nvdro_lcd_width(), nvdro_lcd_height(), g_lut[0]);
+
+ for (y = 0; y < NVDRO_LOGO_SIZE; y++)
+ {
+ int x = 0;
+
+ while (x < NVDRO_LOGO_SIZE)
+ {
+ int n = pos[0];
+ uint16_t color = g_lut[pos[1]];
+
+ while (n-- > 0)
+ {
+ row[x++] = color;
+ }
+
+ pos += 2;
+ }
+
+ nvdro_putrow(x0, y0 + y, NVDRO_LOGO_SIZE, row);
+ }
+}
diff --git a/industry/cnc/nvmpg-dro/nvmpg_dro_main.c
b/industry/cnc/nvmpg-dro/nvmpg_dro_main.c
new file mode 100644
index 000000000..e394841c5
--- /dev/null
+++ b/industry/cnc/nvmpg-dro/nvmpg_dro_main.c
@@ -0,0 +1,601 @@
+/****************************************************************************
+ * apps/industry/cnc/nvmpg-dro/nvmpg_dro_main.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* DRO for the NVMPG pendant. Drop-in replacement for the original
+ * firmware: it speaks the same serial protocol with the NVUM/NVEM
+ * controller.
+ *
+ * Controller -> pendant:
+ * 5A 5A <51 bytes> status frame, no checksum (layout below)
+ * 5A 5B identify request, answered with 01 13
+ *
+ * Pendant -> controller:
+ * key index + 2 key pressed
+ * (key index + 2) | 0x80 key released
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/ioctl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <nuttx/ioexpander/gpio.h>
+
+#include "nvmpg_dro.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NVDRO_SYNC 0x5a
+#define NVDRO_IDENT 0x5b
+#define NVDRO_FRAME_LEN 51
+
+/* Status frame layout (little-endian, packed). The meaning of each field
+ * comes from how the original firmware draws it.
+ */
+
+#define FRM_POS(axis) (4 * (axis)) /* int32, 0.0001 units, axes X..C */
+#define FRM_RESET_LAMP 0x19 /* u8: 0 = green, else red */
+#define FRM_AUX_VALUE 0x1b /* int32, 5 digits, lower right */
+#define FRM_COMM_LAMP 0x1f /* u8: 0 = off, else yellow */
+#define FRM_OVERRIDE(n) (0x20 + (n)) /* u8: FRO, SJR, SRO in % */
+#define FRM_OVR_SELECT 0x24 /* u8: 0 = none, 1..3 = FRO..SRO */
+#define FRM_AXIS_SELECT 0x25 /* u8: 0..5 = X..C, else none */
+#define FRM_STEP 0x26 /* u8: 0..3 = x1 .. x1000 */
+
+#define NVDRO_NAXES 6
+#define NVDRO_NROWS 3
+#define NVDRO_KEYCODE(i) ((uint8_t)((i) + 2))
+#define NVDRO_RELEASE 0x80
+#define NVDRO_POLL_MS 10
+#define NVDRO_LINK_TIMEOUT CONFIG_INDUSTRY_CNC_NVMPG_DRO_LINK_TIMEOUT
+
+/* Screen layout (220x176) */
+
+#define ROW_Y(r) (6 + (r) * 42)
+#define ROW_H 36
+#define AXIS_X 4
+#define AXIS_W 30
+#define VALUE_X 38
+#define VALUE_W 134
+#define VALUE_CHARS 12
+#define OVR_X 176
+#define OVR_W 40
+#define BOTTOM_Y 132
+#define BOTTOM_H 38
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nvdro_s
+{
+ int ttyfd;
+ int keyfd[CONFIG_INDUSTRY_CNC_NVMPG_DRO_NKEYS];
+ pthread_mutex_t lock; /* Protects rxframe/rxseq and TTY writes */
+
+ uint8_t rxframe[NVDRO_FRAME_LEN];
+ uint32_t rxseq; /* Incremented for every complete frame */
+
+ /* Owned by the main thread */
+
+ uint8_t frame[NVDRO_FRAME_LEN];
+ uint8_t shown[NVDRO_FRAME_LEN];
+ bool redraw; /* Ignore 'shown', draw everything */
+ bool linkup;
+ bool shown_linkup;
+ int page; /* 0 = XYZ, 1 = ABC */
+ bool keydown[CONFIG_INDUSTRY_CNC_NVMPG_DRO_NKEYS];
+ bool keyraw[CONFIG_INDUSTRY_CNC_NVMPG_DRO_NKEYS];
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct nvdro_s g_dro;
+
+static const char g_axisname[NVDRO_NAXES] =
+{
+ 'X', 'Y', 'Z', 'A', 'B', 'C'
+};
+
+static const char *const g_ovrname[NVDRO_NROWS] =
+{
+ "FRO%", "SJR%", "SRO%"
+};
+
+static const char *const g_stepname[] =
+{
+ "x1 ", "x10 ", "x100 ", "x1000"
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int32_t get_le32(FAR const uint8_t *p)
+{
+ return (int32_t)((uint32_t)p[0] | ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24));
+}
+
+static bool field_changed(FAR struct nvdro_s *dro, int off, int len)
+{
+ return dro->redraw ||
+ memcmp(&dro->frame[off], &dro->shown[off], len) != 0;
+}
+
+static uint32_t now_ms(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (uint32_t)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
+}
+
+/****************************************************************************
+ * Serial link
+ ****************************************************************************/
+
+static void tty_send(FAR struct nvdro_s *dro, FAR const uint8_t *buf,
+ size_t len)
+{
+ pthread_mutex_lock(&dro->lock);
+ write(dro->ttyfd, buf, len);
+ pthread_mutex_unlock(&dro->lock);
+}
+
+static int tty_open(FAR struct nvdro_s *dro)
+{
+ struct termios tio;
+
+ dro->ttyfd = open(CONFIG_INDUSTRY_CNC_NVMPG_DRO_TTY, O_RDWR | O_NOCTTY);
+ if (dro->ttyfd < 0)
+ {
+ return -errno;
+ }
+
+ /* The board sets USART1 to 115200 8N1, but make sure the port is raw */
+
+ if (tcgetattr(dro->ttyfd, &tio) == 0)
+ {
+ cfmakeraw(&tio);
+ cfsetspeed(&tio, B115200);
+ tcsetattr(dro->ttyfd, TCSANOW, &tio);
+ }
+
+ return 0;
+}
+
+/* Receive thread: same state machine as the original firmware */
+
+static FAR void *rx_thread(FAR void *arg)
+{
+ FAR struct nvdro_s *dro = arg;
+ uint8_t frame[NVDRO_FRAME_LEN];
+ uint8_t buf[16];
+ int state = 0;
+ int idx = 0;
+
+ for (; ; )
+ {
+ ssize_t n = read(dro->ttyfd, buf, sizeof(buf));
+ ssize_t i;
+
+ if (n <= 0)
+ {
+ usleep(10000);
+ continue;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ uint8_t c = buf[i];
+
+ switch (state)
+ {
+ case 0: /* Waiting for the first sync byte */
+ if (c == NVDRO_SYNC)
+ {
+ state = 1;
+ }
+ break;
+
+ case 1: /* Second byte selects frame or identify */
+ if (c == NVDRO_SYNC)
+ {
+ state = 2;
+ idx = 0;
+ }
+ else
+ {
+ state = 0;
+ if (c == NVDRO_IDENT)
+ {
+ static const uint8_t ident[2] =
+ {
+ 0x01, 0x13
+ };
+
+ tty_send(dro, ident, sizeof(ident));
+ }
+ }
+ break;
+
+ default: /* Payload */
+ frame[idx++] = c;
+ if (idx >= NVDRO_FRAME_LEN)
+ {
+ pthread_mutex_lock(&dro->lock);
+ memcpy(dro->rxframe, frame, NVDRO_FRAME_LEN);
+ dro->rxseq++;
+ pthread_mutex_unlock(&dro->lock);
+ state = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ * Keys
+ ****************************************************************************/
+
+static int keys_open(FAR struct nvdro_s *dro)
+{
+ char path[16];
+ int i;
+
+ for (i = 0; i < CONFIG_INDUSTRY_CNC_NVMPG_DRO_NKEYS; i++)
+ {
+ snprintf(path, sizeof(path), "/dev/gpio%d", i);
+ dro->keyfd[i] = open(path, O_RDONLY);
+ if (dro->keyfd[i] < 0)
+ {
+ syslog(LOG_ERR, "nvmpg_dro: cannot open %s: %d\n", path, errno);
+ }
+ }
+
+ return 0;
+}
+
+/* Poll the keys, debounce over two samples and report changes */
+
+static void keys_poll(FAR struct nvdro_s *dro)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_INDUSTRY_CNC_NVMPG_DRO_NKEYS; i++)
+ {
+ bool released = true;
+ bool pressed;
+
+ if (dro->keyfd[i] < 0 ||
+ ioctl(dro->keyfd[i], GPIOC_READ,
+ (unsigned long)((uintptr_t)&released)) < 0)
+ {
+ continue;
+ }
+
+ pressed = !released;
+ if (pressed == dro->keyraw[i] && pressed != dro->keydown[i])
+ {
+ uint8_t code = NVDRO_KEYCODE(i);
+
+ if (!pressed)
+ {
+ code |= NVDRO_RELEASE;
+ }
+
+ tty_send(dro, &code, 1);
+ dro->keydown[i] = pressed;
+ }
+
+ dro->keyraw[i] = pressed;
+ }
+}
+
+/****************************************************************************
+ * Display
+ ****************************************************************************/
+
+static void draw_static(void)
+{
+ int r;
+
+ nvdro_fill(0, 0, nvdro_lcd_width(), nvdro_lcd_height(), NVDRO_BG);
+
+ for (r = 0; r < NVDRO_NROWS; r++)
+ {
+ nvdro_fill(VALUE_X, ROW_Y(r), VALUE_W, ROW_H, NVDRO_PANEL);
+ nvdro_fill(OVR_X, ROW_Y(r), OVR_W, ROW_H, NVDRO_BOX);
+ nvdro_text(OVR_X + 8, ROW_Y(r) + 3, g_ovrname[r], 1, 1,
+ NVDRO_BLACK, NVDRO_BOX);
+ }
+
+ /* Bottom: MPG step | RESET lamp | COMM lamp | aux value */
+
+ nvdro_fill(4, BOTTOM_Y, 62, BOTTOM_H, NVDRO_BOX);
+ nvdro_text(72, BOTTOM_Y, "RESET", 1, 1, NVDRO_WHITE, NVDRO_BG);
+ nvdro_text(116, BOTTOM_Y, "COMM", 1, 1, NVDRO_WHITE, NVDRO_BG);
+ nvdro_fill(156, BOTTOM_Y, 60, BOTTOM_H, NVDRO_BOX);
+}
+
+static void draw_axis_letter(FAR struct nvdro_s *dro, int row)
+{
+ int axis = dro->page * NVDRO_NROWS + row;
+ int sel = dro->frame[FRM_AXIS_SELECT];
+ uint16_t border = (sel == axis) ? NVDRO_YELLOW : NVDRO_BOX;
+ char name[2];
+
+ name[0] = g_axisname[axis];
+ name[1] = '\0';
+
+ nvdro_fill(AXIS_X, ROW_Y(row), AXIS_W, ROW_H, NVDRO_BOX);
+ nvdro_frame(AXIS_X, ROW_Y(row), AXIS_W, ROW_H, 2, border);
+ nvdro_text(AXIS_X + 8, ROW_Y(row) + 8, name, 3, 0,
+ NVDRO_BLACK, NVDRO_BOX);
+}
+
+static void draw_position(FAR struct nvdro_s *dro, int row)
+{
+ int axis = dro->page * NVDRO_NROWS + row;
+ int64_t v = get_le32(&dro->frame[FRM_POS(axis)]);
+ uint16_t fg = dro->linkup ? NVDRO_BLACK : NVDRO_DIM;
+ char num[16];
+ char str[VALUE_CHARS + 4];
+ bool neg = v < 0;
+
+ if (neg)
+ {
+ v = -v;
+ }
+
+ snprintf(num, sizeof(num), "%s%ld.%04ld", neg ? "-" : "",
+ (long)(v / 10000), (long)(v % 10000));
+ snprintf(str, sizeof(str), "%*s", VALUE_CHARS, num);
+
+ nvdro_text(VALUE_X + 1, ROW_Y(row) + 11, str, 2, 1, fg, NVDRO_PANEL);
+}
+
+static void draw_override(FAR struct nvdro_s *dro, int row)
+{
+ bool sel = dro->frame[FRM_OVR_SELECT] == row + 1;
+ char str[8];
+
+ snprintf(str, sizeof(str), "%3u", dro->frame[FRM_OVERRIDE(row)]);
+ nvdro_text(OVR_X + 3, ROW_Y(row) + 14, str, 2, 1,
+ NVDRO_BLACK, NVDRO_BOX);
+ nvdro_fill(OVR_X + 4, ROW_Y(row) + ROW_H - 4, OVR_W - 8, 3,
+ sel ? NVDRO_GREEN : NVDRO_BOX);
+}
+
+static void draw_bottom(FAR struct nvdro_s *dro)
+{
+ uint8_t step;
+ int32_t aux;
+ char str[8];
+
+ if (field_changed(dro, FRM_STEP, 1))
+ {
+ step = dro->frame[FRM_STEP];
+ nvdro_text(7, BOTTOM_Y + 12, step < 4 ? g_stepname[step] : " ? ",
+ 2, 1, NVDRO_BLACK, NVDRO_BOX);
+ }
+
+ if (field_changed(dro, FRM_RESET_LAMP, 1))
+ {
+ nvdro_fill(72, BOTTOM_Y + 14, 34, 12,
+ dro->frame[FRM_RESET_LAMP] ? NVDRO_RED : NVDRO_GREEN);
+ }
+
+ if (field_changed(dro, FRM_COMM_LAMP, 1))
+ {
+ nvdro_fill(116, BOTTOM_Y + 14, 34, 12,
+ dro->frame[FRM_COMM_LAMP] ? NVDRO_YELLOW : NVDRO_LAMP_OFF);
+ }
+
+ if (field_changed(dro, FRM_AUX_VALUE, 4))
+ {
+ aux = get_le32(&dro->frame[FRM_AUX_VALUE]);
+ if (aux > 99999)
+ {
+ aux = 99999;
+ }
+ else if (aux < -9999)
+ {
+ aux = -9999;
+ }
+
+ snprintf(str, sizeof(str), "%5ld", (long)aux);
+ nvdro_text(158, BOTTOM_Y + 12, str, 2, 1, NVDRO_BLACK, NVDRO_BOX);
+ }
+}
+
+/* Redraw only what changed since the last call, like the original */
+
+static void ui_update(FAR struct nvdro_s *dro)
+{
+ uint8_t sel = dro->frame[FRM_AXIS_SELECT];
+ int page = (sel >= NVDRO_NROWS && sel < NVDRO_NAXES) ? 1 : 0;
+ bool axischanged;
+ bool posforce;
+ int r;
+
+ if (page != dro->page)
+ {
+ dro->page = page;
+ dro->redraw = true;
+ }
+
+ if (dro->redraw)
+ {
+ draw_static();
+ }
+
+ axischanged = field_changed(dro, FRM_AXIS_SELECT, 1);
+ posforce = dro->redraw || dro->linkup != dro->shown_linkup;
+
+ for (r = 0; r < NVDRO_NROWS; r++)
+ {
+ int axis = page * NVDRO_NROWS + r;
+
+ if (axischanged)
+ {
+ draw_axis_letter(dro, r);
+ }
+
+ if (posforce || field_changed(dro, FRM_POS(axis), 4))
+ {
+ draw_position(dro, r);
+ }
+
+ if (field_changed(dro, FRM_OVERRIDE(r), 1) ||
+ field_changed(dro, FRM_OVR_SELECT, 1))
+ {
+ draw_override(dro, r);
+ }
+ }
+
+ draw_bottom(dro);
+
+ memcpy(dro->shown, dro->frame, NVDRO_FRAME_LEN);
+ dro->shown_linkup = dro->linkup;
+ dro->redraw = false;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: main
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+ FAR struct nvdro_s *dro = &g_dro;
+ pthread_attr_t attr;
+ pthread_t rxtid;
+ uint32_t seq = 0;
+ uint32_t lastrx;
+ int ret;
+
+ memset(dro, 0, sizeof(*dro));
+ pthread_mutex_init(&dro->lock, NULL);
+
+ ret = nvdro_lcd_open(CONFIG_INDUSTRY_CNC_NVMPG_DRO_LCD);
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "nvmpg_dro: cannot open %s: %d\n",
+ CONFIG_INDUSTRY_CNC_NVMPG_DRO_LCD, ret);
+ return EXIT_FAILURE;
+ }
+
+ ret = tty_open(dro);
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "nvmpg_dro: cannot open %s: %d\n",
+ CONFIG_INDUSTRY_CNC_NVMPG_DRO_TTY, ret);
+ nvdro_lcd_close();
+ return EXIT_FAILURE;
+ }
+
+#ifdef CONFIG_INDUSTRY_CNC_NVMPG_DRO_SPLASH
+ nvdro_logo();
+ usleep(CONFIG_INDUSTRY_CNC_NVMPG_DRO_SPLASH_MS * 1000);
+#endif
+
+ keys_open(dro);
+
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 1024);
+ ret = pthread_create(&rxtid, &attr, rx_thread, dro);
+ if (ret != 0)
+ {
+ syslog(LOG_ERR, "nvmpg_dro: pthread_create failed: %d\n", ret);
+ return EXIT_FAILURE;
+ }
+
+ /* No axis selected and link down until the first frame arrives */
+
+ dro->frame[FRM_AXIS_SELECT] = 0xff;
+ dro->redraw = true;
+ ui_update(dro);
+ lastrx = now_ms();
+
+ for (; ; )
+ {
+ bool newframe = false;
+
+ keys_poll(dro);
+
+ pthread_mutex_lock(&dro->lock);
+ if (dro->rxseq != seq)
+ {
+ seq = dro->rxseq;
+ memcpy(dro->frame, dro->rxframe, NVDRO_FRAME_LEN);
+ newframe = true;
+ }
+
+ pthread_mutex_unlock(&dro->lock);
+
+ if (newframe)
+ {
+ lastrx = now_ms();
+ dro->linkup = true;
+ }
+ else if (dro->linkup && now_ms() - lastrx > NVDRO_LINK_TIMEOUT)
+ {
+ dro->linkup = false;
+ }
+
+ if (newframe || dro->linkup != dro->shown_linkup)
+ {
+ ui_update(dro);
+ }
+
+ usleep(NVDRO_POLL_MS * 1000);
+ }
+
+ return EXIT_SUCCESS;
+}