From 96a78211f6ce82fae642c29a534a440d6a155be8 Mon Sep 17 00:00:00 2001
From: jiajingbin
Date: Fri, 26 Aug 2022 19:03:23 +0800
Subject: [PATCH 1/2] test: add java fast_write_example for 3.0
---
.../example/highvolume/DataBaseMonitor.java | 63 ++++++
.../example/highvolume/FastWriteExample.java | 70 ++++++
.../example/highvolume/MockDataSource.java | 53 +++++
.../com/taos/example/highvolume/ReadTask.java | 58 +++++
.../taos/example/highvolume/SQLWriter.java | 205 ++++++++++++++++++
.../taos/example/highvolume/StmtWriter.java | 4 +
.../taos/example/highvolume/WriteTask.java | 58 +++++
7 files changed, 511 insertions(+)
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java
create mode 100644 docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java
new file mode 100644
index 0000000000..04b149a4b9
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/DataBaseMonitor.java
@@ -0,0 +1,63 @@
+package com.taos.example.highvolume;
+
+import java.sql.*;
+
+/**
+ * Prepare target database.
+ * Count total records in database periodically so that we can estimate the writing speed.
+ */
+public class DataBaseMonitor {
+ private Connection conn;
+ private Statement stmt;
+
+ public DataBaseMonitor init() throws SQLException {
+ if (conn == null) {
+ String jdbcURL = System.getenv("TDENGINE_JDBC_URL");
+ conn = DriverManager.getConnection(jdbcURL);
+ stmt = conn.createStatement();
+ }
+ return this;
+ }
+
+ public void close() {
+ try {
+ stmt.close();
+ } catch (SQLException e) {
+ }
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ }
+ }
+
+ public void prepareDatabase() throws SQLException {
+ stmt.execute("DROP DATABASE IF EXISTS test");
+ stmt.execute("CREATE DATABASE test");
+ stmt.execute("CREATE STABLE test.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)");
+ }
+
+ public Long count() throws SQLException {
+ if (!stmt.isClosed()) {
+ ResultSet result = stmt.executeQuery("SELECT count(*) from test.meters");
+ result.next();
+ return result.getLong(1);
+ }
+ return null;
+ }
+
+ /**
+ * show test.stables;
+ *
+ * name | created_time | columns | tags | tables |
+ * ============================================================================================
+ * meters | 2022-07-20 08:39:30.902 | 4 | 2 | 620000 |
+ */
+ public Long getTableCount() throws SQLException {
+ if (!stmt.isClosed()) {
+ ResultSet result = stmt.executeQuery("show test.stables");
+ result.next();
+ return result.getLong(5);
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java
new file mode 100644
index 0000000000..41b59551ca
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/FastWriteExample.java
@@ -0,0 +1,70 @@
+package com.taos.example.highvolume;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+
+public class FastWriteExample {
+ final static Logger logger = LoggerFactory.getLogger(FastWriteExample.class);
+
+ final static int taskQueueCapacity = 1000000;
+ final static List> taskQueues = new ArrayList<>();
+ final static List readTasks = new ArrayList<>();
+ final static List writeTasks = new ArrayList<>();
+ final static DataBaseMonitor databaseMonitor = new DataBaseMonitor();
+
+ public static void stopAll() {
+ logger.info("shutting down");
+ readTasks.forEach(task -> task.stop());
+ writeTasks.forEach(task -> task.stop());
+ databaseMonitor.close();
+ }
+
+ public static void main(String[] args) throws InterruptedException, SQLException {
+ int readTaskCount = args.length > 0 ? Integer.parseInt(args[0]) : 1;
+ int writeTaskCount = args.length > 1 ? Integer.parseInt(args[1]) : 3;
+ int tableCount = args.length > 2 ? Integer.parseInt(args[2]) : 1000;
+ int maxBatchSize = args.length > 3 ? Integer.parseInt(args[3]) : 3000;
+
+ logger.info("readTaskCount={}, writeTaskCount={} tableCount={} maxBatchSize={}",
+ readTaskCount, writeTaskCount, tableCount, maxBatchSize);
+
+ databaseMonitor.init().prepareDatabase();
+
+ // Create task queues, whiting tasks and start writing threads.
+ for (int i = 0; i < writeTaskCount; ++i) {
+ BlockingQueue queue = new ArrayBlockingQueue<>(taskQueueCapacity);
+ taskQueues.add(queue);
+ WriteTask task = new WriteTask(queue, maxBatchSize);
+ Thread t = new Thread(task);
+ t.setName("WriteThread-" + i);
+ t.start();
+ }
+
+ // create reading tasks and start reading threads
+ int tableCountPerTask = tableCount / readTaskCount;
+ for (int i = 0; i < readTaskCount; ++i) {
+ ReadTask task = new ReadTask(i, taskQueues, tableCountPerTask);
+ Thread t = new Thread(task);
+ t.setName("ReadThread-" + i);
+ t.start();
+ }
+
+ Runtime.getRuntime().addShutdownHook(new Thread(FastWriteExample::stopAll));
+
+ long lastCount = 0;
+ while (true) {
+ Thread.sleep(10000);
+ long numberOfTable = databaseMonitor.getTableCount();
+ long count = databaseMonitor.count();
+ logger.info("numberOfTable={} count={} speed={}", numberOfTable, count, (count - lastCount) / 10);
+ lastCount = count;
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java
new file mode 100644
index 0000000000..6fe83f002e
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/MockDataSource.java
@@ -0,0 +1,53 @@
+package com.taos.example.highvolume;
+
+import java.util.Iterator;
+
+/**
+ * Generate test data
+ */
+class MockDataSource implements Iterator {
+ private String tbNamePrefix;
+ private int tableCount;
+ private long maxRowsPerTable = 1000000000L;
+
+ // 100 milliseconds between two neighbouring rows.
+ long startMs = System.currentTimeMillis() - maxRowsPerTable * 100;
+ private int currentRow = 0;
+ private int currentTbId = -1;
+
+ // mock values
+ String[] location = {"LosAngeles", "SanDiego", "Hollywood", "Compton", "San Francisco"};
+ float[] current = {8.8f, 10.7f, 9.9f, 8.9f, 9.4f};
+ int[] voltage = {119, 116, 111, 113, 118};
+ float[] phase = {0.32f, 0.34f, 0.33f, 0.329f, 0.141f};
+
+ public MockDataSource(String tbNamePrefix, int tableCount) {
+ this.tbNamePrefix = tbNamePrefix;
+ this.tableCount = tableCount;
+ }
+
+ @Override
+ public boolean hasNext() {
+ currentTbId += 1;
+ if (currentTbId == tableCount) {
+ currentTbId = 0;
+ currentRow += 1;
+ }
+ return currentRow < maxRowsPerTable;
+ }
+
+ @Override
+ public String next() {
+ long ts = startMs + 100 * currentRow;
+ int groupId = currentTbId % 5 == 0 ? currentTbId / 5 : currentTbId / 5 + 1;
+ StringBuilder sb = new StringBuilder(tbNamePrefix + "_" + currentTbId + ","); // tbName
+ sb.append(ts).append(','); // ts
+ sb.append(current[currentRow % 5]).append(','); // current
+ sb.append(voltage[currentRow % 5]).append(','); // voltage
+ sb.append(phase[currentRow % 5]).append(','); // phase
+ sb.append(location[currentRow % 5]).append(','); // location
+ sb.append(groupId); // groupID
+
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java
new file mode 100644
index 0000000000..a6fcfed1d2
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/ReadTask.java
@@ -0,0 +1,58 @@
+package com.taos.example.highvolume;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+
+class ReadTask implements Runnable {
+ private final static Logger logger = LoggerFactory.getLogger(ReadTask.class);
+ private final int taskId;
+ private final List> taskQueues;
+ private final int queueCount;
+ private final int tableCount;
+ private boolean active = true;
+
+ public ReadTask(int readTaskId, List> queues, int tableCount) {
+ this.taskId = readTaskId;
+ this.taskQueues = queues;
+ this.queueCount = queues.size();
+ this.tableCount = tableCount;
+ }
+
+ /**
+ * Assign data received to different queues.
+ * Here we use the suffix number in table name.
+ * You are expected to define your own rule in practice.
+ *
+ * @param line record received
+ * @return which queue to use
+ */
+ public int getQueueId(String line) {
+ String tbName = line.substring(0, line.indexOf(',')); // For example: tb1_101
+ String suffixNumber = tbName.split("_")[1];
+ return Integer.parseInt(suffixNumber) % this.queueCount;
+ }
+
+ @Override
+ public void run() {
+ logger.info("started");
+ Iterator it = new MockDataSource("tb" + this.taskId, tableCount);
+ try {
+ while (it.hasNext() && active) {
+ String line = it.next();
+ int queueId = getQueueId(line);
+ taskQueues.get(queueId).put(line);
+ }
+ } catch (Exception e) {
+ logger.error("Read Task Error", e);
+ }
+ }
+
+ public void stop() {
+ logger.info("stop");
+ this.active = false;
+ }
+}
\ No newline at end of file
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java
new file mode 100644
index 0000000000..c2989acdbe
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/SQLWriter.java
@@ -0,0 +1,205 @@
+package com.taos.example.highvolume;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A helper class encapsulate the logic of writing using SQL.
+ *
+ * The main interfaces are two methods:
+ *
+ * - {@link SQLWriter#processLine}, which receive raw lines from WriteTask and group them by table names.
+ * - {@link SQLWriter#flush}, which assemble INSERT statement and execute it.
+ *
+ *
+ * There is a technical skill worth mentioning: we create table as needed when "table does not exist" error occur instead of creating table automatically using syntax "INSET INTO tb USING stb".
+ * This ensure that checking table existence is a one-time-only operation.
+ *
+ *
+ *
+ */
+public class SQLWriter {
+ final static Logger logger = LoggerFactory.getLogger(SQLWriter.class);
+
+ private Connection conn;
+ private Statement stmt;
+
+ /**
+ * current number of buffered records
+ */
+ private int bufferedCount = 0;
+ /**
+ * Maximum number of buffered records.
+ * Flush action will be triggered if bufferedCount reached this value,
+ */
+ private int maxBatchSize;
+
+
+ /**
+ * Maximum SQL length.
+ */
+ private int maxSQLLength;
+
+ /**
+ * Map from table name to column values. For example:
+ * "tb001" -> "(1648432611249,2.1,114,0.09) (1648432611250,2.2,135,0.2)"
+ */
+ private Map tbValues = new HashMap<>();
+
+ /**
+ * Map from table name to tag values in the same order as creating stable.
+ * Used for creating table.
+ */
+ private Map tbTags = new HashMap<>();
+
+ public SQLWriter(int maxBatchSize) {
+ this.maxBatchSize = maxBatchSize;
+ }
+
+
+ /**
+ * Get Database Connection
+ *
+ * @return Connection
+ * @throws SQLException
+ */
+ private static Connection getConnection() throws SQLException {
+ String jdbcURL = System.getenv("TDENGINE_JDBC_URL");
+ return DriverManager.getConnection(jdbcURL);
+ }
+
+ /**
+ * Create Connection and Statement
+ *
+ * @throws SQLException
+ */
+ public void init() throws SQLException {
+ conn = getConnection();
+ stmt = conn.createStatement();
+ stmt.execute("use test");
+ ResultSet rs = stmt.executeQuery("show variables");
+ while (rs.next()) {
+ String configName = rs.getString(1);
+ if ("maxSQLLength".equals(configName)) {
+ maxSQLLength = Integer.parseInt(rs.getString(2));
+ logger.info("maxSQLLength={}", maxSQLLength);
+ }
+ }
+ }
+
+ /**
+ * Convert raw data to SQL fragments, group them by table name and cache them in a HashMap.
+ * Trigger writing when number of buffered records reached maxBachSize.
+ *
+ * @param line raw data get from task queue in format: tbName,ts,current,voltage,phase,location,groupId
+ */
+ public void processLine(String line) throws SQLException {
+ bufferedCount += 1;
+ int firstComma = line.indexOf(',');
+ String tbName = line.substring(0, firstComma);
+ int lastComma = line.lastIndexOf(',');
+ int secondLastComma = line.lastIndexOf(',', lastComma - 1);
+ String value = "(" + line.substring(firstComma + 1, secondLastComma) + ") ";
+ if (tbValues.containsKey(tbName)) {
+ tbValues.put(tbName, tbValues.get(tbName) + value);
+ } else {
+ tbValues.put(tbName, value);
+ }
+ if (!tbTags.containsKey(tbName)) {
+ String location = line.substring(secondLastComma + 1, lastComma);
+ String groupId = line.substring(lastComma + 1);
+ String tagValues = "('" + location + "'," + groupId + ')';
+ tbTags.put(tbName, tagValues);
+ }
+ if (bufferedCount == maxBatchSize) {
+ flush();
+ }
+ }
+
+
+ /**
+ * Assemble INSERT statement using buffered SQL fragments in Map {@link SQLWriter#tbValues} and execute it.
+ * In case of "Table does not exit" exception, create all tables in the sql and retry the sql.
+ */
+ public void flush() throws SQLException {
+ StringBuilder sb = new StringBuilder("INSERT INTO ");
+ for (Map.Entry entry : tbValues.entrySet()) {
+ String tableName = entry.getKey();
+ String values = entry.getValue();
+ String q = tableName + " values " + values + " ";
+ if (sb.length() + q.length() > maxSQLLength) {
+ executeSQL(sb.toString());
+ logger.warn("increase maxSQLLength or decrease maxBatchSize to gain better performance");
+ sb = new StringBuilder("INSERT INTO ");
+ }
+ sb.append(q);
+ }
+ executeSQL(sb.toString());
+ tbValues.clear();
+ bufferedCount = 0;
+ }
+
+ private void executeSQL(String sql) throws SQLException {
+ try {
+ stmt.executeUpdate(sql);
+ } catch (SQLException e) {
+ // convert to error code defined in taoserror.h
+ int errorCode = e.getErrorCode() & 0xffff;
+ if (errorCode == 0x362 || errorCode == 0x218) {
+ // Table does not exist
+ createTables();
+ executeSQL(sql);
+ } else {
+ logger.error("Execute SQL: {}", sql);
+ throw e;
+ }
+ } catch (Throwable throwable) {
+ logger.error("Execute SQL: {}", sql);
+ throw throwable;
+ }
+ }
+
+ /**
+ * Create tables in batch using syntax:
+ *
+ * CREATE TABLE [IF NOT EXISTS] tb_name1 USING stb_name TAGS (tag_value1, ...) [IF NOT EXISTS] tb_name2 USING stb_name TAGS (tag_value2, ...) ...;
+ *
+ */
+ private void createTables() throws SQLException {
+ StringBuilder sb = new StringBuilder("CREATE TABLE ");
+ for (String tbName : tbValues.keySet()) {
+ String tagValues = tbTags.get(tbName);
+ sb.append("IF NOT EXISTS ").append(tbName).append(" USING meters TAGS ").append(tagValues).append(" ");
+ }
+ String sql = sb.toString();
+ try {
+ stmt.executeUpdate(sql);
+ } catch (Throwable throwable) {
+ logger.error("Execute SQL: {}", sql);
+ throw throwable;
+ }
+ }
+
+ public boolean hasBufferedValues() {
+ return bufferedCount > 0;
+ }
+
+ public int getBufferedCount() {
+ return bufferedCount;
+ }
+
+ public void close() {
+ try {
+ stmt.close();
+ } catch (SQLException e) {
+ }
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java
new file mode 100644
index 0000000000..8ade06625d
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/StmtWriter.java
@@ -0,0 +1,4 @@
+package com.taos.example.highvolume;
+
+public class StmtWriter {
+}
diff --git a/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java b/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java
new file mode 100644
index 0000000000..de9e5463d7
--- /dev/null
+++ b/docs/examples/java/src/main/java/com/taos/example/highvolume/WriteTask.java
@@ -0,0 +1,58 @@
+package com.taos.example.highvolume;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.BlockingQueue;
+
+class WriteTask implements Runnable {
+ private final static Logger logger = LoggerFactory.getLogger(WriteTask.class);
+ private final int maxBatchSize;
+
+ // the queue from which this writing task get raw data.
+ private final BlockingQueue queue;
+
+ // A flag indicate whether to continue.
+ private boolean active = true;
+
+ public WriteTask(BlockingQueue taskQueue, int maxBatchSize) {
+ this.queue = taskQueue;
+ this.maxBatchSize = maxBatchSize;
+ }
+
+ @Override
+ public void run() {
+ logger.info("started");
+ String line = null; // data getting from the queue just now.
+ SQLWriter writer = new SQLWriter(maxBatchSize);
+ try {
+ writer.init();
+ while (active) {
+ line = queue.poll();
+ if (line != null) {
+ // parse raw data and buffer the data.
+ writer.processLine(line);
+ } else if (writer.hasBufferedValues()) {
+ // write data immediately if no more data in the queue
+ writer.flush();
+ } else {
+ // sleep a while to avoid high CPU usage if no more data in the queue and no buffered records, .
+ Thread.sleep(100);
+ }
+ }
+ if (writer.hasBufferedValues()) {
+ writer.flush();
+ }
+ } catch (Exception e) {
+ String msg = String.format("line=%s, bufferedCount=%s", line, writer.getBufferedCount());
+ logger.error(msg, e);
+ } finally {
+ writer.close();
+ }
+ }
+
+ public void stop() {
+ logger.info("stop");
+ this.active = false;
+ }
+}
\ No newline at end of file
From 4ff7bb654a13e260ad75dde07cd72a70c6cb81c3 Mon Sep 17 00:00:00 2001
From: jiajingbin
Date: Fri, 26 Aug 2022 19:05:51 +0800
Subject: [PATCH 2/2] test: add highvolume.webp for 3.0
---
.../en/07-develop/03-insert-data/highvolume.webp | Bin 0 -> 7308 bytes
.../zh/07-develop/03-insert-data/highvolume.webp | Bin 0 -> 7308 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 docs/en/07-develop/03-insert-data/highvolume.webp
create mode 100644 docs/zh/07-develop/03-insert-data/highvolume.webp
diff --git a/docs/en/07-develop/03-insert-data/highvolume.webp b/docs/en/07-develop/03-insert-data/highvolume.webp
new file mode 100644
index 0000000000000000000000000000000000000000..46dfc74ae3b0043c591ff930c62251da49cae7ad
GIT binary patch
literal 7308
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
literal 0
HcmV?d00001
diff --git a/docs/zh/07-develop/03-insert-data/highvolume.webp b/docs/zh/07-develop/03-insert-data/highvolume.webp
new file mode 100644
index 0000000000000000000000000000000000000000..46dfc74ae3b0043c591ff930c62251da49cae7ad
GIT binary patch
literal 7308
zcmV;79CPDRNk&G58~^}UMM6+kP&iC?8~^|>J;OQx^$LQvZIhNi>vh|I9Y91(0N+-{
z@q~s&%Rp;e0%Z<1bLVc{L=nxhVdzfv5b1mZbdtw+o+)`|6nWX*#S=C0zJ@c^WFPpr
zEYSaFI+j#3Ya?shaJ~$*0#6C=#yD{Yk3ZBZU2-;(swkodNj=A?n6K0l5ux$_jNnmOW3=ZyI4DN
zcX!`~Pz59u6oew(=@6r>?Vr*}`lXf7)0v?w1VZ7VQV@zzjO)4{^$qM5%nj}Fp6;2u
zOP(GV#@)STeGKS7BsP*1Nf8IVa}2#Om|gRu$N&F1De^wDYmsf+wryMI%eHOXwins9
z?KZpq*q!D4cKZE(-+#`wefN1}Ot!5#=00a+O`RRle@JW_Ig;edapP-SQ{KV6bwBKX8z?_!pP0EcEX=X6ou0PF$lpU?uNYWWqXoeEoJ*ErL
z?p)6Z;{1BVhA4K;M3PRdNGnwk0_miz7wO0ei<|XfW-KjO++t?lg-NI=WaY}&hv_Ov
zf=EL^sa1Ze&-GzC3`vqBsMISjS;l*uievBE>ZcyvZ`(t(83^2^(9A7rU#9sP%hUv|
zI;a#iwP*m`p!v|W^L*T7n?zXCal1EGai8|c-eYddw`VYFC0Vn@$fhID{E_sJ9uWo-|*Q^DHA6T@kBRuVV?RB>x4CxN9J?)
zZoBq2ze{1f%EVvL_&yZ-Bzgtr2RmHYu3ufuzKvD^VLd#u^GZ{T3;AMyZ>?GW=ly2C
zqr?SSSOmPsBbetd!*XcS@DOb>1u_+LwUD4BN1sB%-4Fm&qtCf+%>1%;J}grczRLHx
zbfczn61{{aKYUYpr1n7nZ7F`ymYuohHT)BAKL%!SdUk{x6g7HIW74G*hvpznv
z-q6Fap=*e7qRTW$89TkYV#?ZDu^-cdq7qjJY6HVlA8Y{peO#s8(%Q?UHj&xQZ=&PU
z#JK6#l^)Y>TapO2gU5%GrWkyzZw@8|$l
z{A#NQ(uWVB3YPtLk2+i9kDoE=<>tnW)RG#YH0Q2{HV|;Tm>}jx2~b#xO(ouPpn2_~
zr&zS8in*~%axp(itLJ$@y)$WkbqDr1Y=Ng%7WnGz|7v5N`WW#>d8Fg_F9-i4!4q#a
zh2SFv;Fzj^{88oW%KIxVt@wO}JLS)o+gTQ%%oC-rm-?dQ<`VA~f2i1n3j#rrwS`|V
z^jE=e3mnY9BHxR7fB7ZP`P{p6EzJ3Jj+@y}X4{(e{Vb1WzMSckjO#N5xjH}qYvdS`
zBu5_R$nBgTFp2Gc;y4E=L3Gj2P|(I_CE1;yF67Jp%WRu!RokYg_q+5R;)CYKj23GD
zL@e7{wd>ni@%ROip{-rJY@f69v)vc$Ew_Kr!8Z>7boAP5j@LLj?(~AQ|DC_%Vw20y
zuU)(5`f)cOx!vLJjQiUMuL3yd{F>l8U^PRMMB-r<9kEkLG7C-Bc~5>#+(?C!Raviu
zCEDz1tL(JRtIvMV-E`i_Y4W8bxtnKE>B0r9^=7acI9U6I6eUI|*|mWf;f5cUVOM+M(Sh-=)wj)tp9L06nT7X~_At4K#?cHtHPaYLvpqz<#ER5uGtcmUp46+dCHWs`M{sc-a3g^k+4UfVe2^j=cE^Zyi+Cd-XTI`jE#TUG^i_
zN^{t9ra68+P~TIeI^WNv1F%#VZdyei5yLNq3$RvHHjU{fzrM4qsYKnQ@B2*-niNePUVH>dX9*1?`jRf-~Mp=WlV`}GIR
zKM+6XcqJ;#s(F_+uphUvnnjVX$Rk0a$5tN_1_Kuyq~>JsK~F~{CCsc5%L;rGuZjAP
zZ)`Y0NVWjn?NRUIh87hXf*Pc%G6~uG{PgNEErX*3i;2wSldhS)iai$e^`UffD~vR1
z>eeo_AW0_E{)k}-)+AJUky3&ATgi|E=Nv)=vcjpPCOC~R1
zG`L_GEuEK2ho%kxD9RJ@GOdb&j)acPC?va5TBrQHbHCJsJSK*V~{xL&Ew!gLO0Dfaq#mMHsdD7;{
z4kpr-hXQ3rt{WSdihpRI&I3q?E;$O>!t6rJ_*)357FV9)dnQ(hWCmO&Yk?{uF0@PI
z4rIcV8iR<$g$d;&ng&-XTB1sb3vE-o1-Wpg$08zeVSL%}rbJw)Y>g@*F04!C222Xd
zjzdJ^!dGS1%8BgwtqrP#xX>!aHFzmPxmOU8xUlq#u(EJwRa;aE+X{9d3AbY&r=W&3
zf)SDonc6VL-*qHRAMj7|&*5;Sv#MJ@U~4h%-y98q$&B9o-80}m?fa+__7-F4qkY;Z
z5)hG2VIs|+bpd80KYx^QckvZs!$vR7=L1}6fHK@IqJ^+0PH(A_j~eppGC(%7jbK!A
zIW#Fm1%nudQw(c7zN`l%TefeE(*u&gArs*7hYn15;Y
zz>N`&9)uS#wkt6NZT}Z=3s8v?4TS(?PDMVy2B`->UjXnsf+&^#W#-Ms#8$o8C?SeM
z25}}?i*k%sqmw@w-O6OBF<3@0E(6Eb>r#LVj_}XPaEc)`!{6I+8ltPM_CE#O6!2xh
z+5p?NC_|@0d6|;KoX6Z4dlpA--J2yG?aquOjU?Fe#^oi{Y*}DZPBD~F-tK9b>w_D2
z=I#Uz$OT)0pgvXK*1cJyGo>MzCW}!^W{`cMkXhnR14b~A+dW7jDHu*MP<86lBQ?Dq
zti4ym1I9_WB^N7Lm>Q)ik;=&z2DkDd5F;BlHnDSxzz5p1&IbE-z^x
z#opcZ1=a*ClGWQ=K?vKwdz
zj{*FZU@M?M14&HzzX%z@@WAe&W9@U>Yt5f%nV@sy@ivLr>5uAV^qY*)m^;
zK!{#1!d+j{n=Za?p+&4a{=DC*vtN4g&pUsmLx9-eaRZrXP8Z*-yGPZKimq6)h<$}M
zs!M=a*sRU7w5E%%Wq4v*xvEBqbEUdk{hMt%1voFMnD>@+ri-tX)IeTcAY_nNCKo9P
z;=l+Hbr|jF;_LF_>8}ywBnsrfHd9Ok02$=gj$5UA4-y=WRx=&U0Nvm(W5$hFo;Z2RwCOWv&6zu&
z67M+w%56Zu3gH>#y<-ok37&}#Vf)jFF23f}eZC$i0tt^BSq+~R#l$7|>(Z@PzX3yr
zjT$o{E&Hn}GiJ@Nvt)UTH5;~U+qLK5PsdK2xp4Wo8+YzK?(^c+yANM}{Q3_NC~(kV
zAwq@@6E6J2Zhy(KMvftgG6j)AUSHpUU`FXaR4k4>#fzPn`hvuB=tmdd^c#mvM_1KM
zr2uhQO*fbcrh{o<@Sp+x`}XP8qkGpbojSH}*JfSI=FOB8<(o8WSifGK+BIubt5UgQ
zg>q#}e^H`%(ISNkYfif{S!kuD^?ws(#=k1t2NH@CpCf++_+PjJ^=%q%2
zfBwFMzpE~10+2!8u*OUKsn)#Y9{5gH`nigha
z%cTI);A&xzok8B7N;IN43RC@LX0Ah|IE)fyvMVhggg^)gai&lb-rL0&;+*5sU>yg6
zS%DC40LUP(dzx2%fnav*Ph|xxL8PsT4UUl+%?#owAvP-8!fUDP?9u)%z5@|80FXic
zGJFRJQLXK8r-&0GKO7c^43Y)4VUipfrBSd|Z%%lxI?f&&W#y5#Q=-m-RBLLF9Y_w>
zj*{yeciO^tPKyWrJ5UYfYM{FKtt!ZehEmbllXvS#1~_v)@QC@Bx;K<`_BvVzPObs+
z!I8Q*8aaEmsN(DiK}v;df(HYke?wJgFOIkhdX!mkH0A6K=RP$ZF7_n?l2wrfM^nz;
z*QvNn_rUP~bRkImfUG=-rL1t(*}D=K0(DdQw#wI-0v!cO^b)Al#eRw(3u?Pcw}-vl
zBERgPH8>YX{(($^Nv+O6H-}1jEh>x#g5Hkcmb3SvOT*dKKG?(@=B4g5n2L0BsN3Yk
zc%sedg66jN$pvSxUnn@BwzI{FhZsZ`-@4R_mjY&le|tTQ65>;W?mlnFbU|%peiHD_
zR0im0hQm}p8J8IlpTnJE!eU3zPzpJF#mw1!9+^sZ>Q9x{eIqyI)lu*PQ2yoAZ$}A1
z$!E$qdrHsgNsmk>Q(B9|6FWWRhk!E0kB@CLIxXKy5od2`B#1T=+4xYz*-O3O%VA{Y
zLk(xItTjuekd+TLoV|5N#8V(k6FT7Rjg1N61|eG$8sO|@*1w|(vNu`aJ9|~_t}KKk
zC5wcj>N$HiK7fR?k+9^U@M@gB$@hr_?~Mc|iX6^fF5^z?BB99_R^%+0*p0*{J%fS7
z4h2%ftOO9CGl#QBWGHCLUNv=`J>o)RHwGV7-`QhSY+Ncmr3%X4c6;0243m83Hz3wm
z8qT|8N^H_m%45$twqHbID3qz2X;;^Dhh`{2N-Mgm2FmF$90ITl|D+lrT?tXY}m9jZ;VTg5KJ?I
zxGuFAfpoe(Hr1lD7Z?0@WHO9yYB5z20!n2)$O;(~P{F2!;@Yt6L9_Gjg8#kGGXU*;
zK{9P}_FNgrL>?Hw48myNN}xjD+qy#-*lOrnL+w13b5)%^f&kyamj$@u?5VT$d3K+!
zfkZaW;EuERGACKxvH47U5Q4tI<#~5Pcs~Hw<=QPU&k2t!0((!>dB`ihrWV)d-3fQ<
z+A|>6<$CpzUtQsR=QvzC(a>u%3MjiLh3x3-QZrnfXSd9&ksIRL7jy0+bAw9{m*Bzxdze#wb;&U=-
z1rM640&xa;DHZ$AXAFOiAT~FH#zI!}ZxXf0x$EiLU^PRCPqOUP;b~22EAN$|4g2L<
zs`n(%KZ_c5xxNQ`)s4!f4oWl
zw!q#m*Ws!m4Up&j2uhOW3g5*Dzc8}7WOegYnba!3OH)t-Tz_jy8xt=ph>hlk=@UlB
zfQv)uLI?TR-uv~oYfWsZW*2_&4WI3#0R3ETfk?8fxs;adPjq;=Q`D#+1kVwPGr*M%
zS>Fd_)OTBf26T|u_}uu+zx~T2oep)m29jji{pTTl4WU0+-5la`DAcMQV`^)Yw^X)m
zb}iC|4)Pk`$OqT@GoeCVt|_y2YD3yjl}QMu#UabD54@v;Ur0O`sY*bt%e9Abo~kQ7
zWcBqRWZmv$8P_TwD4i)acJ8;lDU#T|5V8+aHUYja*FgvtS9WO%wr+1;WcbhVp^N%R
z<>*XXROy
z^Rl|whsiY4vsUxb3mRd7s61MMOG<=k>QQ%kL>))$g^G=--$csV-%A$a!`>~N%k2dWpf;0eB9<8b~
zt#W`2a8Ga-kPHN)&o=#;j-p$oZF}N^a{buZATwqzEeJszWLF-|o86@aJOs=RRsuqI
zlR${BJes|L-#oHxzW{J`Z?HNrP%4k+pP2gyO|Lt^VqiGf4G29Lk}HqqUTng_T#Ihh
zoeKm1JXaS6Lgmpe1HN}Yw3O;P;CFm(@Mi_+zYtw{G
zpwX~URUC#TZh{z1Obv+S1Uw@g3coq%B!|gIlVPKZY!s<*aCKa#{GUL>kvIubqy(|^
z?f!JwZZBZv!Die;cd7=;hIubaWJbJ~Cdw3&2{Ii}l~yM|dba_Dza3q`Cz!uyF}Kj4
zDp50|z0lPp9fm-%vmjGii-g?cognZFrnJZ8VD2+
zfE}YJiv4uixnDUu!YXc$Wg7enURfX=qbFEkUxtKXRJi92@Okh#UUrP0_;K1Cqvf*S
zR_G;A0c9QW{1+U+1+xp=*z6}O;AQtPt-(t>$ZO^5yZhh!>U)=tqymCud?4drFfX$k
zfq7h)z$Uog`i>Uh#U12RZck8!4E{%ci4LHCJu8CE0G#nJ;N_MCKcp>BJdWlxJA3Jy$6jLKV>#$c30+g!RH~3f8mWG7|AXv
z9f7kWJ=Db>UoAYhgZ#_KzccAKD@{j&B+P2=4(f+ck;ML
zAO3=V7b(WQaZmr2@npUt*DKbjx48Ku3wu8_V%(HjTK$G@&i{1_kO0UZ|ALWCm;Lob
zU?+Q~a}Ewus5SZcV4JVz4X^DWf56w(yx5>G%$z)S_<&xW+caxjw_1ggh0kY8pEA);
zcSnm5GO!@4I+UbF;@((fZ4)VW(2;LAbKEW1PdF0|5Xpa8CLF
literal 0
HcmV?d00001