commit 21eca7fdcfd904c1a55ff0921db9300988884eca Author: sjchen <13418980720@163.com> Date: Tue Dec 31 16:42:48 2024 +0800 first commit diff --git a/.cloudbase/container/debug.json b/.cloudbase/container/debug.json new file mode 100644 index 0000000..0d44458 --- /dev/null +++ b/.cloudbase/container/debug.json @@ -0,0 +1 @@ +{"containers":[],"config":{}} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..12b6a07 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.wxml": "html", + "*.wxss": "css" + } +} \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..a0f82cf --- /dev/null +++ b/app.js @@ -0,0 +1,5 @@ +App({ + onLaunch: function () { + + } +}) diff --git a/app.json b/app.json new file mode 100644 index 0000000..ce56547 --- /dev/null +++ b/app.json @@ -0,0 +1,15 @@ +{ + "pages": [ + "index/index", + "ecg/index", + "ecg2/index" + ], + "window": { + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#fff", + "navigationBarTitleText": "WeChat", + "navigationBarTextStyle": "black" + }, + "permission": {}, + "sitemapLocation": "sitemap.json" +} \ No newline at end of file diff --git a/app.wxss b/app.wxss new file mode 100644 index 0000000..e69de29 diff --git a/ecg/hrdata.js b/ecg/hrdata.js new file mode 100644 index 0000000..8a04b60 --- /dev/null +++ b/ecg/hrdata.js @@ -0,0 +1,29172 @@ +export const hrDataArray = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -5730, + -5508, + -5484, + -5640, + -5916, + -6264, + -6618, + -6924, + -7152, + -7278, + -7308, + -7260, + -7158, + -7032, + -6906, + -6798, + -6702, + -6612, + -6528, + -6432, + -6312, + -6168, + -6000, + -5640, + -5472, + -5322, + -5208, + -5118, + -5052, + -5004, + -4956, + -4902, + -4830, + -4746, + -4644, + -4530, + -4416, + -4308, + -4200, + -4110, + -4026, + -3948, + -3876, + -3810, + -3738, + -3666, + -3594, + -3522, + -3450, + -3384, + -3318, + -3246, + -3180, + -3102, + -3018, + -2922, + -2814, + -2700, + -2568, + -2442, + -2316, + -2196, + -2094, + -2010, + -1956, + -1926, + -1932, + -1962, + -2010, + -2076, + -2154, + -2232, + -2298, + -2340, + -2358, + -2346, + -2310, + -2244, + -2160, + -2064, + -1968, + -1890, + -1788, + -1770, + -1770, + -1782, + -1794, + -1800, + -1788, + -1746, + -1656, + -1566, + -1470, + -1374, + -1266, + -1158, + -1062, + -972, + -894, + -822, + -756, + -696, + -636, + -576, + -498, + -432, + -348, + -282, + -186, + -120, + -60, + 0, + 60, + 120, + 180, + 246, + 276, + 336, + 390, + 450, + 504, + 558, + 612, + 660, + 714, + 732, + 780, + 834, + 882, + 930, + 978, + 1026, + 1074, + 1122, + 1164, + 1212, + 1260, + 1302, + 1344, + 1392, + 1476, + 1518, + 1560, + 1602, + 1644, + 1680, + 1722, + 1758, + 1800, + 1836, + 1908, + 1944, + 1980, + 2016, + 2046, + 2118, + 2148, + 2178, + 2202, + 2226, + 2250, + 2274, + 2292, + 2316, + 2334, + 2358, + 2382, + 2400, + 2424, + 2430, + 2448, + 2472, + 2496, + 2520, + 2556, + 2598, + 2640, + 2688, + 2730, + 2778, + 2820, + 2838, + 2880, + 2916, + 2958, + 2994, + 3036, + 3072, + 3108, + 3126, + 3156, + 3186, + 3216, + 3252, + 3270, + 3306, + 3336, + 3366, + 3396, + 3444, + 3462, + 3474, + 3480, + 3480, + 3480, + 3474, + 3474, + 3474, + 3474, + 3480, + 3492, + 3504, + 3516, + 3534, + 3552, + 3570, + 3588, + 3612, + 3630, + 3666, + 3702, + 3756, + 3798, + 3858, + 3906, + 3954, + 3990, + 4038, + 4074, + 4110, + 4146, + 4176, + 4206, + 4236, + 4266, + 4296, + 4320, + 4344, + 4362, + 4380, + 4392, + 4404, + 4422, + 4440, + 4458, + 4470, + 4488, + 4512, + 4530, + 4548, + 4572, + 4590, + 4614, + 4632, + 4656, + 4680, + 4704, + 4752,4782, + 4818, + 4848, + 4872, + 4902, + 4932, + 4962, + 4992, + 5028, + 5058, + 5094, + 5124, + 5160, + 5190, + 5220, + 5256, + 5286, + 5316, + 5346, + 5376, + 5406, + 5436, + 5466, + 5490, + 5520, + 5544, + 5574, + 5598, + 5622, + 5652, + 5676, + 5700, + 5730, + 5754, + 5778, + 5802, + 5832, + 5856, + 5880, + 5904, + 5928, + 5958, + 5982, + 6006, + 6030, + 6054, + 6078, + 6096, + 6120, + 6144, + 6162, + 6180, + 6204, + 6222, + 6240, + 6258, + 6270, + 6306, + 6318, + 6336, + 6348, + 6360, + 6372, + 6378, + 6396, + 6414, + 6426, + 6432, + 6444, + 6450, + 6456, + 6462, + 6468, + 6474, + 6474, + 6474, + 6468, + 6462, + 6456, + 6444, + 6426, + 6396, + 6384, + 6366, + 6348, + 6336, + 6294, + 6270, + 6240, + 6210, + 6180, + 6150, + 6126, + 6102, + 6078, + 6060, + 6042, + 6024, + 6006, + 5988, + 5952, + 5946, + 5958, + 5970, + 5982, + 5982, + 5964, + 5922, + 5844, + 5736, + 5610, + 5466, + 5322, + 5190, + 5082, + 4998, + 4908, + 4896, + 4890, + 4884, + 4878, + 4860, + 4836, + 4806, + 4770, + 4734, + 4698, + 4674, + 4638, + 4602, + 4560, + 4518, + 4476, + 4434, + 4392, + 4356, + 4326, + 4284, + 4254, + 4218, + 4182, + 4140, + 4074, + 4062, + 4020, + 3978, + 3936, + 3894, + 3846, + 3786, + 3738, + 3696, + 3648, + 3624, + 3582, + 3534, + 3486, + 3438, + 3390, + 3342, + 3294, + 3228, + 3180, + 3126, + 3078, + 3024, + 2958, + 2898, + 2838, + 2784, + 2730, + 2676, + 2622, + 2568, + 2466, + 2418, + 2364, + 2316, + 2250, + 2202, + 2148, + 2100, + 2052, + 2040, + 1992, + 1944, + 1902, + 1860, + 1818, + 1776, + 1734, + 1692, + 1650, + 1614, + 1572, + 1536, + 1494, + 1458, + 1422, + 1386, + 1350, + 1314, + 1278, + 1242, + 1206, + 1170, + 1140, + 1104, + 1068, + 1038, + 1002, + 972, + 942, + 912, + 882, + 852, + 828, + 804, + 774, + 750, + 732, + 708, + 690, + 672, + 654, + 636, + 624, + 612, + 582, + 570, + 564, + 558, + 546, + 540, + 540, + 534, + 534, + 534, + 534, + 534, + 540, + 540, + 546, + 540, + 540, + 546, + 546, + 546, + 540, + 540, + 522, + 516, + 516, + 510, + 510, + 504, + 504, + 504, + 486, + 486, + 456, + 456, + 450, + 456, + 468, + 480, + 498, + 516, + 534, + 546, + 564, + 576, + 588, + 594, + 606, + 606, + 612, + 618, + 618, + 618, + 618, + 618, + 618, + 618, + 612, + 606, + 600, + 594, + 588, + 576, + 570, + 546, + 534, + 522, + 504, + 492, + 474, + 462, + 444, + 432, + 414, + 396, + 384, + 366, + 348, + 330, + 318, + 300, + 288, + 282, + 270, + 258, + 240, + 228, + 216, + 204, + 192, + 186, + 174, + 168, + 156, + 150, + 144, + 138, + 132, + 126, + 120, + 120, + 114, + 114, + 108, + 102, + 102, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 84, + 78, + 72, + 66, + 42, + 30, + 18, + 6, + -18, + -24, + -24, + -36, + -42, + -54, + -60, + -66, + -72, + -78, + -84, + -90, + -96, + -96, + -102, + -108, + -108, + -108, + -114, + -114, + -114, + -120, + -120, + -120, + -120, + -120, + -108, + -108, + -114, + -114, + -114, + -114, + -120, + -120, + -120, + -114, + -108, + -102, + -102, + -102, + -102, + -108, + -102, + -90, + -72, + -30, + 18, + 84, + 156, + 228, + 288, + 336, + 354, + 348, + 306, + 240, + 150, + 42, + -66, + -258, + -318, + -354, + -360, + -348, + -318, + -276, + -228, + -186, + -150, + -114, + -90, + -72, + -60, + -48, + -36, + -30, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + 0, + 6, + 18, + 30, + 36, + 42, + 42, + 36, + 18, + 18, + 12, + 6, + 6, + 0, + 0, + -6, + -18, + -18, + -24, + -30, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 6, + 12, + 18, + 24, + 30, + 36, + 42, + 42, + 48, + 54, + 60, + 60, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 72, + 72, + 66, + 66, + 60, + 54, + 48, + 36, + 30, + 18, + 6, + -6, + -18, + -36, + -54, + -72, + -90, + -108, + -126, + -144, + -162, + -186, + -204, + -228, + -246, + -270, + -288, + -312, + -330, + -348, + -366, + -384, + -402, + -420, + -414, + -426, + -438, + -456, + -468, + -492, + -504, + -516, + -534, + -546, + -558, + -570, + -582, + -594, + -612, + -624, + -636, + -654, + -666, + -684, + -702, + -720, + -738, + -750, + -768, + -786, + -804, + -822, + -840, + -858, + -876, + -894, + -906, + -924, + -936, + -954, + -966, + -978, + -990, + -1002, + -1014, + -1020, + -1032, + -1038, + -1044, + -1050, + -1056, + -1062, + -1068, + -1068, + -1074, + -1080, + -1080, + -1086, + -1086, + -1086, + -1092, + -1092, + -1098, + -1098, + -1104, + -1110, + -1110, + -1116, + -1128, + -1128, + -1134, + -1140, + -1146, + -1158, + -1164, + -1170, + -1170, + -1176, + -1182, + -1188, + -1194, + -1200, + -1206, + -1212, + -1212, + -1218, + -1224, + -1230, + -1236, + -1236, + -1242, + -1248, + -1248, + -1254, + -1254, + -1260, + -1266, + -1266, + -1272, + -1272, + -1278, + -1278, + -1284, + -1284, + -1284, + -1290, + -1290, + -1296, + -1296, + -1296, + -1302, + -1302, + -1302, + -1302, + -1308, + -1308, + -1308, + -1308, + -1308, + -1308, + -1314, + -1320, + -1320, + -1320, + -1314, + -1314, + -1314, + -1314, + -1314, + -1308, + -1308, + -1308, + -1308, + -1308, + -1308, + -1308, + -1308, + -1314, + -1302, + -1308, + -1308, + -1314, + -1320, + -1326, + -1326, + -1332, + -1338, + -1356, + -1362, + -1368, + -1362, + -1356, + -1356, + -1356, + -1356, + -1356, + -1356, + -1350, + -1344, + -1332, + -1320, + -1308, + -1302, + -1290, + -1284, + -1278, + -1266, + -1248, + -1212, + -1176, + -1122, + -1068, + -1020, + -972, + -948, + -942, + -960, + -1008, + -1074, + -1158, + -1248, + -1338, + -1416, + -1476, + -1512, + -1518, + -1488, + -1458, + -1416, + -1374, + -1338, + -1308, + -1284, + -1266, + -1260, + -1248, + -1236, + -1230,-1224, + -1218, + -1218, + -1212, + -1212, + -1206, + -1200, + -1194, + -1188, + -1182, + -1176, + -1158, + -1146, + -1140, + -1134, + -1134, + -1128, + -1122, + -1134, + -1128, + -1128, + -1122, + -1110, + -1104, + -1104, + -1104, + -1104, + -1104, + -1104, + -1104, + -1104, + -1104, + -1104, + -1104, + -1098, + -1098, + -1098, + -1098, + -1098, + -1098, + -1092, + -1092, + -1086, + -1086, + -1086, + -1080, + -1074, + -1056, + -1050, + -1050, + -1044, + -1038, + -1032, + -1026, + -1020, + -1026, + -1020, + -1008, + -1002, + -996, + -984, + -978, + -966, + -954, + -948, + -936, + -924, + -912, + -900, + -888, + -876, + -864, + -852, + -840, + -828, + -816, + -804, + -792, + -780, + -774, + -762, + -750, + -738, + -732, + -726, + -714, + -702, + -696, + -684, + -672, + -666, + -654, + -648, + -636, + -630, + -618, + -612, + -600, + -594, + -588, + -576, + -570, + -564, + -552, + -546, + -534, + -522, + -516, + -504, + -498, + -492, + -480, + -474, + -468, + -456, + -450, + -444, + -438, + -432, + -420, + -408, + -402, + -396, + -390, + -378, + -372, + -366, + -360, + -354, + -348, + -342, + -342, + -336, + -330, + -324, + -324, + -318, + -312, + -312, + -306, + -300, + -294, + -288, + -282, + -276, + -270, + -264, + -258, + -252, + -246, + -228, + -216, + -210, + -204, + -192, + -186, + -174, + -168, + -156, + -150, + -138, + -126, + -120, + -108, + -84, + -78, + -66, + -54, + -42, + -30, + -18, + -18, + -6, + 12, + 24, + 36, + 48, + 60, + 72, + 90, + 102, + 114, + 126, + 144, + 156, + 168, + 180, + 192, + 210, + 222, + 234, + 246, + 258, + 258, + 270, + 282, + 294, + 306, + 312, + 324, + 336, + 342, + 354, + 360, + 372, + 378, + 390, + 396, + 402, + 414, + 420, + 426, + 444, + 450, + 450, + 456, + 462, + 468, + 474, + 474, + 480, + 486, + 492, + 498, + 498, + 504, + 510, + 510, + 516, + 522, + 534, + 534, + 546, + 552, + 552, + 558, + 564, + 570, + 588, + 600, + 624, + 642, + 666, + 684, + 702, + 720, + 738, + 756, + 774, + 804, + 840, + 888, + 936, + 990, + 1044, + 1092, + 1122, + 1134, + 1122, + 1092, + 1038, + 972, + 900, + 822, + 756, + 708, + 672, + 660, + 672, + 696, + 738, + 780, + 828, + 870, + 906, + 936, + 960, + 978, + 990, + 1014, + 1026, + 1050, + 1056, + 1062, + 1074, + 1080, + 1086, + 1092, + 1098, + 1104, + 1116, + 1128, + 1134, + 1146, + 1152, + 1164, + 1170, + 1176, + 1182, + 1194, + 1200, + 1206, + 1218, + 1224, + 1230, + 1236, + 1248, + 1254, + 1260, + 1266, + 1278, + 1284, + 1290, + 1296, + 1302, + 1314, + 1320, + 1326, + 1332, + 1338, + 1350, + 1356, + 1362, + 1368, + 1374, + 1380, + 1386, + 1392, + 1398, + 1404, + 1410, + 1416, + 1422, + 1428, + 1434, + 1440, + 1446, + 1452, + 1458, + 1464, + 1470, + 1476, + 1482, + 1482, + 1488, + 1494, + 1494, + 1500, + 1506, + 1506, + 1512, + 1512, + 1518, + 1518, + 1524, + 1524, + 1524, + 1530, + 1530, + 1530, + 1536, + 1536, + 1536, + 1536, + 1542, + 1542, + 1542, + 1542, + 1548, + 1548, + 1548, + 1548, + 1548, + 1548, + 1554, + 1554, + 1554, + 1554, + 1554, + 1554, + 1560, + 1560, + 1560, + 1560, + 1560, + 1560, + 1560, + 1560, + 1566, + 1566, + 1566, + 1566, + 1566, + 1566, + 1572, + 1572, + 1572, + 1572, + 1578, + 1578, + 1584, + 1584, + 1584, + 1590, + 1590, + 1596, + 1596, + 1602, + 1602, + 1608, + 1608, + 1614, + 1620, + 1620, + 1626, + 1632, + 1632, + 1638, + 1644, + 1650, + 1650, + 1656, + 1662, + 1662, + 1668, + 1674, + 1680, + 1680, + 1686, + 1692, + 1698, + 1698, + 1704, + 1710, + 1716, + 1722, + 1722, + 1728, + 1734, + 1740, + 1746, + 1746, + 1752, + 1758, + 1764, + 1770, + 1770, + 1776, + 1782, + 1788, + 1788, + 1794, + 1800, + 1800, + 1806, + 1812, + 1812, + 1818, + 1818, + 1824, + 1824, + 1824, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1842, + 1836, + 1836, + 1836, + 1836, + 1836, + 1836, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1830, + 1836, + 1836, + 1836, + 1836, + 1836, + 1836, + 1836, + 1836, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1842, + 1854, + 1854, + 1854, + 1854, + 1854, + 1842, + 1842, + 1848, + 1854, + 1866, + 1872, + 1878, + 1878, + 1878, + 1878, + 1878, + 1890, + 1920, + 1956, + 2010, + 2076, + 2142, + 2208, + 2268, + 2304, + 2316, + 2298, + 2250, + 2172, + 2082, + 1986, + 1890, + 1806, + 1746, + 1704, + 1692, + 1698, + 1722, + 1752, + 1788, + 1818, + 1848, + 1860, + 1872, + 1878, + 1878, + 1878, + 1878, + 1890, + 1908, + 1914, + 1920, + 1926, + 1926, + 1932, + 1932, + 1932, + 1932, + 1932, + 1932, + 1926, + 1932, + 1932, + 1932, + 1932, + 1932, + 1902, + 1896, + 1890, + 1884, + 1878, + 1872, + 1890, + 1884, + 1878, + 1866, + 1860, + 1848, + 1836, + 1824, + 1812, + 1800, + 1788, + 1770, + 1758, + 1746, + 1728, + 1716, + 1704, + 1686, + 1674, + 1656, + 1644, + 1632, + 1614, + 1602, + 1590, + 1572, + 1560, + 1548, + 1530, + 1518, + 1500, + 1482, + 1464, + 1452, + 1428, + 1410, + 1398, + 1374, + 1356, + 1332, + 1308, + 1278, + 1254, + 1224, + 1200, + 1170, + 1140, + 1110, + 1080, + 1050, + 1020, + 996, + 966, + 912, + 882, + 858, + 828, + 804, + 774, + 750, + 726, + 726, + 702, + 678, + 660, + 636, + 618, + 600, + 576, + 558, + 540, + 522, + 504, + 486, + 468, + 450, + 432, + 420, + 402, + 384, + 366, + 348, + 330, + 312, + 294, + 270, + 252, + 234, + 216, + 198, + 180, + 162, + 144, + 126, + 108, + 90, + 72, + 54, + 42, + 24, + 6, + -12, + -30, + -42, + -60, + -78, + -90, + -108, + -120, + -138, + -150, + -180, + -192, + -216, + -234, + -246, + -258, + -270, + -282, + -300, + -306, + -318, + -330, + -342, + -354, + -360, + -372, + -378, + -384, + -390, + -414, + -420, + -420, + -426, + -426, + -426, + -426, + -426, + -420, + -420, + -414, + -408, + -402, + -396, + -396, + -390, + -384, + -378, + -378, + -372, + -372, + -372, + -378, + -378, + -378, + -390, + -390, + -390, + -396, + -396, + -402, + -408, + -408, + -414, + -420, + -426, + -432, + -438, + -444, + -450, + -462, + -468, + -474, + -486, + -492, + -498, + -504, + -510, + -516, + -522, + -528, + -534, + -540, + -546, + -552, + -552, + -558, + -564, + -564, + -570, + -570, + -576, + -576, + -576, + -576, + -582, + -582, + -582, + -582, + -582, + -582, + -588, + -588, + -588, + -588, + -588, + -576, + -582, + -582, + -582, + -582, + -576, + -576, + -576, + -576, + -576, + -576, + -576, + -570, + -564, + -558, + -546, + -528, + -510, + -480, + -450, + -408, + -366, + -270, + -228, + -192, + -168, + -168, + -180, + -216, + -270, + -336, + -408, + -486, + -558, + -612, + -654, + -672, + -672, + -654, + -618, + -582, + -540, + -510, + -486, + -468, + -462, + -474, + -480, + -486, + -492, + -492, + -486, + -486, + -480, + -480, + -480, + -474, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -486, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -480, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -474, + -468, + -468, + -468, + -468, + -468, + -468, + -468, + -462, + -462, + -462, + -462, + -462, + -462, + -462, + -456, + -456, + -456, + -450, + -450, + -444, + -444, + -438, + -438, + -438, + -432, + -432, + -426, + -426, + -426, + -420, + -420, + -420, + -420, + -420, + -426, + -426, + -426, + -432, + -432, + -438, + -438, + -444, + -444, + -450, + -450, + -456, + -456, + -462, + -462, + -468, + -468, + -474, + -474, + -474, + -474, + -480, + -480, + -468, + -468, + -468, + -468, + -468, + -462, + -462, + -474, + -468, + -468, + -462, + -462, + -456, + -456, + -450, + -450, + -444, + -438, + -438, + -432, + -432, + -426, + -426, + -420, + -420, + -414, + -414, + -414, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -402, + -402, + -402, + -402, + -402, + -390, + -390, + -390, + -384, + -384, + -384, + -378, + -378, + -372, + -372, + -372, + -366, + -366, + -360, + -360, + -354, + -354, + -348, + -348, + -342, + -342, + -336, + -336, + -330, + -330, + -324, + -324, + -318, + -318, + -312, + -312, + -306, + -306, + -300, + -300, + -294, + -294, + -288, + -288, + -282, + -282,-276, + -276, + -270, + -270, + -264, + -264, + -258, + -258, + -258, + -252, + -252, + -252, + -246, + -246, + -246, + -240, + -240, + -240, + -240, + -240, + -240, + -234, + -234, + -234, + -234, + -234, + -234, + -228, + -222, + -216, + -210, + -210, + -204, + -198, + -192, + -192, + -192, + -192, + -192, + -192, + -192, + -192, + -186, + -174, + -162, + -138, + -108, + -66, + -18, + 30, + 78, + 126, + 162, + 186, + 192, + 180, + 150, + 102, + 36, + -36, + -108, + -216, + -252, + -264, + -264, + -252, + -228, + -204, + -186, + -174, + -162, + -168, + -168, + -162, + -156, + -150, + -144, + -150, + -150, + -168, + -162, + -162, + -156, + -156, + -150, + -150, + -144, + -144, + -144, + -138, + -138, + -138, + -138, + -132, + -132, + -132, + -126, + -126, + -120, + -120, + -114, + -108, + -102, + -102, + -96, + -90, + -84, + -78, + -72, + -66, + -60, + -54, + -48, + -42, + -36, + -30, + -24, + -18, + -12, + -12, + -6, + -6, + 0, + 6, + 6, + 12, + 18, + 18, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 30, + 30, + 36, + 36, + 42, + 48, + 48, + 54, + 60, + 66, + 66, + 72, + 78, + 78, + 84, + 84, + 90, + 90, + 90, + 96, + 96, + 96, + 102, + 102, + 108, + 108, + 108, + 114, + 114, + 114, + 120, + 120, + 120, + 126, + 126, + 126, + 132, + 132, + 132, + 138, + 138, + 138, + 144, + 144, + 144, + 150, + 150, + 150, + 156, + 156, + 156, + 156, + 162, + 162, + 162, + 168, + 168, + 168, + 168, + 168, + 174, + 174, + 174, + 174, + 174, + 174, + 180, + 180, + 180, + 180, + 180, + 180, + 186, + 186, + 186, + 186, + 186, + 192, + 192, + 192, + 192, + 198, + 192, + 198, + 198, + 204, + 204, + 210, + 210, + 216, + 216, + 222, + 222, + 222, + 228, + 228, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 234, + 228, + 228, + 228, + 228, + 228, + 228, + 222, + 222, + 222, + 222, + 222, + 222, + 222, + 222, + 222, + 222, + 222, + 222, + 216, + 216, + 228, + 228, + 222, + 228, + 228, + 222, + 216, + 210, + 204, + 204, + 210, + 216, + 222, + 228, + 234, + 234, + 234, + 234, + 234, + 234, + 240, + 258, + 288, + 330, + 384, + 450, + 570, + 612, + 636, + 642, + 612, + 564, + 492, + 408, + 312, + 222, + 144, + 84, + 48, + 30, + 30, + 48, + 72, + 108, + 138, + 162, + 186, + 198, + 210, + 210, + 216, + 216, + 216, + 222, + 222, + 222, + 216, + 216, + 210, + 210, + 210, + 210, + 204, + 204, + 198, + 198, + 192, + 198, + 192, + 192, + 192, + 186, + 186, + 180, + 180, + 180, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 150, + 150, + 150, + 150, + 150, + 144, + 144, + 144, + 144, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 102, + 102,102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 96, + 96, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 78, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 36, + 36, + 36, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 60, + 66, + 72, + 72, + 78, + 84, + 102, + 126, + 162, + 210, + 270, + 330, + 390, + 444, + 480, + 492, + 480, + 444, + 390, + 312, + 228, + 72, + 12, + -30, + -60, + -66, + -60, + -42, + -18, + 6, + 30, + 48, + 66, + 78, + 84, + 96, + 102, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 108, + 114, + 114, + 114, + 114, + 120, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 114, + 120, + 120, + 120, + 120, + 120, + 126, + 126, + 126, + 126, + 126, + 132, + 132, + 132, + 132, + 132, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 138, + 138, + 138, + 138, + 144, + 144, + 144, + 144, + 150, + 150, + 150, + 156, + 156, + 156, + 162, + 162, + 168, + 168, + 168, + 174, + 174, + 180, + 180, + 180, + 180, + 186, + 186, + 186, + 192, + 192, + 192, + 192, + 198, + 198, + 198, + 198, + 198, + 198, + 198, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 204, + 198, + 198, + 198, + 198, + 198, + 198, + 198, + 192, + 192, + 192, + 192, + 192, + 186, + 186, + 186, + 186, + 180, + 180, + 180, + 180, + 180, + 174, + 174, + 174, + 174, + 174, + 174, + 168, + 168, + 168, + 168, + 168, + 168, + 174, + 162, + 162, + 168, + 168, + 168, + 174, + 174, + 174, + 180, + 186, + 180, + 186, + 192, + 198, + 204, + 210, + 216, + 222, + 222, + 228, + 228, + 228, + 240, + 240, + 240, + 240, + 234, + 228, + 228, + 222, + 216, + 210, + 204, + 192, + 186, + 174, + 168, + 168, + 162, + 156, + 156, + 150, + 144, + 138, + 132, + 132, + 126, + 126, + 120, + 120, + 114, + 114, + 108, + 108, + 102, + 102, + 102, + 96, + 96, + 90, + 102, + 102, + 102, + 102, + 102, + 102, + 90, + 84, + 78, + 78, + 72, + 78, + 78, + 84, + 90, + 90, + 96, + 108, + 120, + 144, + 174, + 216, + 270, + 324, + 378, + 426, + 456, + 468, + 456, + 426, + 366, + 216, + 132, + 54, + -12, + -54, + -84, + -96, + -90, + -72, + -48, + -24, + 0, + 18, + 30, + 42, + 54, + 60, + 60, + 66, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 84, + 84, + 78, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -6, + 6, + 30, + 114, + 168, + 222, + 282, + 336, + 378, + 396, + 390, + 360, + 306, + 234, + 144, + 54, + -30, + -102, + -156, + -186, + -192, + -180, + -156, + -120, + -90, + -60, + -36, + -24, + -24, + -30, + -36, + -42, + -48, + -30, + -30, + -24, + -24, + -24, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 36, + 36, + 42, + 42, + 48, + 48, + 54, + 54, + 54, + 48, + 54, + 54, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 24, + 24, + 30, + 30, + 30, + 30, + 18, + 6, + -6, + -12, + -12, + -12, + -6, + 6, + 18, + 30, + 48, + 66, + 84, + 108, + 138, + 174, + 216, + 264, + 306, + 342, + 360, + 366, + 348, + 306, + 246, + 168, + 90, + 6, + -66, + -120, + -156, + -168, + -168, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 0, + 0, + 0, + -6, + -6, + -12, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -24, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -30, + -30, + -24, + -18, + -12, + -6, + 0, + 12, + 18, + 30, + 48, + 78, + 108, + 144, + 192, + 234, + 270, + 300, + 318, + 312, + 294, + 246, + 186, + 114, + 30, + -48, + -120, + -180, + -222, + -240, + -246, + -234, + -204, + -174, + -138, + -108, + -78, + -54, + -42, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -30, + -30, + -30, + -30, + -24, + -30, + -30, + -30, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0,0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + 0, + 18, + 36, + 96, + 138, + 186, + 234, + 276, + 312, + 336, + 342, + 330, + 294, + 240, + 174, + 96, + 18, + -54, + -120, + -162, + -180, + -186, + -168, + -144, + -108, + -78, + -48, + -24, + -12, + -6, + -6, + -12, + -12, + 0, + 12, + 18, + 18, + 24, + 24, + 18, + 12, + 12, + 12, + 6, + 6, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 42, + 42, + 42, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 48, + 48,48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 120, + 120, + 120, + 120, + 120, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 102, + 102, + 102, + 102, + 108, + 114, + 120, + 126, + 126, + 132, + 132, + 132, + 138, + 144, + 162, + 180, + 216, + 252, + 300, + 354, + 408, + 450, + 486, + 510, + 510, + 492, + 456, + 396, + 330, + 252, + 174, + 108, + 48, + 12, + -12, + -12, + 24, + 60, + 90, + 120, + 138, + 156, + 156, + 156, + 150, + 144, + 144, + 156, + 150, + 150, + 150, + 156, + 156, + 156, + 162, + 162, + 168, + 168, + 168, + 174, + 174, + 174, + 180, + 180, + 180, + 180, + 186, + 186, + 186, + 186, + 192, + 192, + 192, + 198, + 198, + 198, + 198, + 204, + 204, + 204, + 204, + 210, + 210, + 210, + 210, + 216, + 216, + 216, + 222, + 222, + 222, + 228, + 228, + 228, + 234, + 234, + 240, + 240, + 240, + 246, + 246, + 252, + 252, + 252, + 258, + 258, + 258, + 264, + 264, + 264, + 270, + 270, + 270, + 276, + 276, + 276, + 276, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 300, + 300, + 300, + 300, + 300, + 306, + 306, + 306, + 312, + 312, + 312, + 318, + 318, + 324, + 324, + 330, + 330, + 330, + 336, + 336, + 342, + 342, + 348, + 348, + 348, + 354, + 354, + 360, + 360, + 366, + 366, + 366, + 372, + 372, + 378, + 378, + 384, + 384, + 390, + 390, + 396, + 396, + 402, + 402, + 408, + 408, + 414, + 414, + 420, + 426, + 432, + 432, + 438, + 438, + 444, + 444, + 450, + 450, + 456, + 456, + 456, + 462, + 462, + 468, + 468, + 468, + 474, + 474, + 474, + 480, + 480, + 480, + 486, + 486, + 486, + 492, + 492, + 492, + 492, + 498, + 498, + 498, + 504, + 504, + 504, + 510, + 510, + 510, + 510, + 516, + 516, + 516, + 516, + 516, + 522, + 522, + 522, + 522, + 522, + 528, + 528, + 528, + 528, + 528, + 534, + 534, + 534, + 534, + 540, + 534, + 540, + 540, + 540, + 546, + 546, + 552, + 552, + 552, + 558, + 558, + 564, + 564, + 564, + 570, + 570, + 570, + 570, + 570, + 570, + 570, + 570, + 570, + 570, + 564, + 564, + 564, + 558, + 558, + 552, + 552, + 546, + 546, + 540, + 540, + 534, + 528, + 528, + 522, + 522, + 516, + 516, + 510, + 504, + 504, + 498, + 498, + 492, + 486, + 486, + 486, + 480, + 474, + 474, + 468, + 462, + 456, + 450, + 444, + 426, + 420, + 426, + 426, + 432, + 438, + 438, + 438, + 426, + 420, + 408, + 396, + 396, + 408, + 426, + 462, + 510, + 564, + 624, + 684, + 732, + 762, + 774, + 762, + 720, + 666, + 588, + 510, + 420, + 342, + 276, + 228, + 192, + 180, + 180, + 192, + 210, + 234, + 252, + 270, + 282, + 288, + 288, + 282, + 282, + 276, + 276, + 264, + 258, + 252, + 246, + 234, + 228, + 222, + 210, + 204, + 204, + 198, + 192, + 192, + 186, + 174, + 168, + 162, + 162, + 156, + 150, + 144, + 138, + 132, + 126, + 120, + 120, + 114, + 108, + 102, + 96, + 96, + 90, + 78, + 72, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 24, + 24, + 18, + 18, + 12, + 12, + 6, + 6, + 0, + 0, + -6, + -6, + -12, + -18, + -18, + -24, + -24, + -30, + -36, + -36, + -42, + -42, + -48, + -54, + -54, + -60, + -60, + -66, + -72, + -72, + -78, + -78, + -84, + -90, + -90, + -96, + -96, + -102, + -108, + -108, + -114, + -114, + -120, + -120, + -126, + -126, + -132, + -132, + -138, + -138, + -144, + -144, + -150, + -150, + -156, + -156, + -162, + -162, + -168, + -168, + -174, + -174, + -174, + -180, + -180, + -186, + -186, + -186, + -192, + -192, + -192, + -198, + -198, + -204, + -204, + -204, + -210, + -210, + -210, + -216, + -216, + -216, + -222, + -222, + -228, + -228, + -228, + -228, + -234, + -234, + -234, + -234, + -234, + -234, + -234, + -240, + -240, + -240, + -240, + -240, + -246, + -246, + -246, + -252, + -252, + -252, + -258, + -264, + -264, + -270, + -276, + -282, + -288, + -294, + -300, + -312, + -318, + -324, + -336, + -348, + -360, + -372, + -384, + -396, + -408, + -420, + -438, + -462, + -480, + -510, + -534, + -564, + -594, + -618, + -642, + -660, + -672, + -684, + -690, + -690, + -696, + -696, + -702, + -720, + -732, + -738, + -738, + -750, + -756, + -762, + -768, + -774, + -780, + -792, + -798, + -804, + -804, + -810, + -816, + -822, + -822, + -828, + -828, + -828, + -834, + -834, + -846, + -846, + -852, + -858, + -870, + -876, + -882, + -888, + -894, + -906, + -912, + -924, + -942, + -948, + -960, + -972, + -978, + -990, + -996, + -1008, + -1020, + -1026, + -1038, + -1044, + -1050, + -1062, + -1068, + -1080, + -1086, + -1092, + -1104, + -1110, + -1116, + -1128, + -1140, + -1146, + -1152, + -1176, + -1188, + -1188, + -1194, + -1200, + -1200, + -1206, + -1212, + -1218, + -1206, + -1218, + -1224, + -1242, + -1266, + -1296, + -1326, + -1362, + -1386, + -1410, + -1422, + -1434, + -1428, + -1422, + -1404, + -1380, + -1350, + -1314, + -1278, + -1236, + -1188, + -1146, + -1104, + -1068, + -1038, + -1056, + -1092, + -1152, + -1230, + -1320, + -1410, + -1506, + -1584, + -1650, + -1692, + -1716, + -1710, + -1692, + -1662, + -1626, + -1596, + -1572, + -1560, + -1560, + -1566, + -1590, + -1614, + -1632, + -1650, + -1668, + -1680, + -1686, + -1644, + -1644, + -1638, + -1638, + -1638, + -1632, + -1626, + -1620, + -1614, + -1608, + -1602, + -1596, + -1584, + -1572, + -1566, + -1554, + -1542, + -1530, + -1518, + -1506, + -1512, + -1500, + -1482, + -1464, + -1446, + -1428, + -1416, + -1398, + -1380, + -1374, + -1362, + -1320, + -1296, + -1266, + -1236, + -1206, + -1176, + -1140, + -1104, + -1074, + -1044, + -1020, + -996, + -972, + -972, + -954, + -936, + -918, + -912, + -894, + -870, + -852, + -834, + -816, + -816, + -798, + -780, + -762, + -744, + -732, + -714, + -702, + -684, + -672, + -660, + -648, + -630, + -618, + -606, + -594, + -588, + -576, + -564, + -558, + -546, + -540, + -528, + -522, + -516, + -510, + -504, + -480, + -474, + -468, + -468, + -462, + -456, + -456, + -450, + -456, + -456, + -450, + -450, + -444, + -444, + -438, + -438, + -438, + -438, + -432, + -432, + -432, + -432, + -432, + -432, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -426, + -420, + -420, + -420, + -420, + -420, + -420, + -420, + -420, + -420, + -414, + -414, + -414, + -414, + -414, + -414, + -414, + -414, + -414, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -408, + -402, + -402, + -402, + -402, + -402, + -396, + -402, + -396, + -396, + -396, + -396, + -390, + -390, + -390, + -390, + -384, + -384, + -378, + -378, + -372, + -366, + -372, + -366, + -366, + -366, + -360, + -354, + -348, + -336, + -318, + -306, + -288, + -270, + -258, + -240, + -222, + -204, + -186, + -174, + -156, + -132, + -126, + -120, + -114, + -108, + -108, + -108, + -108, + -102, + -102, + -96, + -96, + -96, + -90, + -84, + -78, + -72, + -66, + -60, + -48, + -42, + -30, + -18, + -6, + 0, + 6, + 18, + 24, + 36, + 54, + 66, + 72, + 84, + 90, + 96, + 96, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 126, + 132, + 138, + 144, + 150, + 156, + 162, + 174, + 180, + 186, + 204, + 210, + 216, + 228, + 234, + 240, + 252, + 258, + 264, + 276, + 282, + 288, + 306, + 306, + 312, + 318, + 336, + 348, + 366, + 378, + 390, + 396, + 402, + 402, + 402, + 402, + 402, + 402, + 402, + 414, + 426, + 444, + 480, + 516, + 564, + 618, + 672, + 714, + 744, + 756, + 750, + 714, + 660, + 582, + 498, + 408, + 330, + 258, + 210, + 186, + 174, + 186, + 210, + 246, + 276, + 312, + 336, + 354, + 372, + 372, + 372, + 372, + 366, + 366, + 360, + 360, + 360, + 360, + 360, + 360, + 360, + 360, + 366, + 366, + 372, + 384, + 384, + 390, + 396, + 396, + 402, + 402, + 408, + 414, + 414, + 420, + 426, + 426, + 432, + 438, + 438, + 444, + 444, + 444, + 444, + 450, + 450, + 450, + 450, + 450, + 450, + 450, + 444, + 444, + 444, + 438, + 438, + 438, + 432, + 432, + 426, + 426, + 420, + 420, + 414, + 414, + 408, + 408, + 408, + 402, + 402, + 402, + 396, + 396, + 396, + 390, + 390, + 390, + 384, + 384, + 384, + 384, + 378, + 378, + 378, + 372, + 372, + 372, + 372, + 372, + 366, + 366, + 360, + 360, + 354, + 354, + 348, + 342, + 342, + 336, + 330, + 330, + 324, + 318, + 318, + 312, + 306, + 306, + 300, + 300, + 294, + 294, + 300, + 300, + 300, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 300, + 300, + 300, + 300, + 294, + 300, + 300, + 306, + 306, + 312, + 318, + 324, + 324, + 330, + 342, + 348, + 354, + 360, + 366, + 378, + 384, + 390, + 396, + 402, + 408, + 414, + 414, + 414, + 414, + 420, + 420, + 420, + 420, + 426, + 426, + 426, + 420, + 420, + 408, + 408, + 408, + 408, + 402, + 402, + 402, + 402, + 396, + 396, + 390, + 390, + 384, + 384, + 378, + 378, + 378, + 372, + 372, + 366, + 360, + 360, + 354, + 348, + 342, + 336, + 330, + 324, + 318, + 312, + 312, + 306, + 300, + 300, + 294, + 294, + 288, + 288, + 288, + 282, + 276, + 276, + 276, + 282, + 282, + 288, + 288, + 294, + 294, + 300, + 306, + 306, + 306, + 312, + 312, + 324, + 318, + 318, + 318, + 318, + 318, + 318, + 318, + 318, + 318, + 312, + 300, + 300, + 300, + 300, + 294, + 294, + 288, + 288, + 288, + 282, + 282, + 276, + 276, + 270, + 270, + 264, + 264, + 258, + 252, + 252, + 246, + 246, + 240, + 234, + 228, + 228, + 222, + 216, + 210, + 204, + 204, + 198, + 192, + 186, + 186, + 180, + 174, + 174, + 168, + 162, + 150, + 150, + 144, + 138, + 150, + 144, + 132, + 126, + 126, + 120, + 120, + 114, + 114, + 120, + 120, + 114, + 108, + 102, + 96, + 96, + 102, + 108, + 114, + 126, + 132, + 144, + 150, + 150, + 156, + 156, + 162, + 174, + 192, + 216, + 252, + 300, + 390, + 426, + 450, + 456, + 432, + 384, + 318, + 234, + 138, + 48, + -42, + -108, + -156, + -180, + -180, + -162, + -132, + -90, + -48, + -12, + 18, + 42, + 54, + 60, + 60, + 60, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 90, + 90, + 96, + 102, + 102, + 108, + 108, + 114, + 114, + 120, + 120, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 120, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 102, + 96, + 96, + 90, + 90, + 90, + 84, + 84, + 78, + 78, + 78, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -54, + -54, + -54, + -60, + -60, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -60, + -60, + -60, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -66, + -54, + -36, + -6, + 30, + 72, + 120, + 168, + 210, + 246, + 258, + 252, + 222, + 168, + 96, + 18, + -66, + -144, + -210, + -258, + -282, + -282, + -270, + -240, + -204, + -168, + -138, + -114, + -102, + -96, + -102, + -102, + -108, + -114, + -102, + -96, + -90, + -78, + -66, + -60, + -48, + -42, + -36, + -36, + -42, + -48, + -48, + -60, + -66, + -72, + -72, + -72, + -78, + -78, + -84, + -84, + -84, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -36, + -30, + -30, + -18, + -12, + -6, + -6, + -12, + -18, + -30, + -36, + -36, + -36, + -30, + -24, + -12, + 6, + 24, + 48, + 72, + 108, + 150, + 192, + 246, + 294, + 336, + 372, + 384, + 378, + 354, + 306, + 234, + 156, + 72, + -6, + -72, + -126, + -150, + -162, + -150, + -132, + -102, + -72, + -48, + -24, + -6, + 0, + 6, + 12, + 18, + 24, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + -6, + 0, + 0, + -6, + -6, + -12, + -18, + -24, + -24, + -30, + -36, + -36, + -42, + -48, + -48, + -54, + -60, + -60, + -66, + -66, + -72, + -72, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -72, + -72, + -66, + -66, + -72, + -84, + -90, + -90, + -90, + -84, + -78, + -66, + -60, + -54, + -48, + -48, + -42, + -30, + -18, + 12, + 96, + 150, + 204, + 252, + 282, + 300, + 288, + 252, + 192, + 120, + 36, + -48, + -126, + -186, + -228, + -246, + -246, + -234, + -210, + -186, + -156, + -132, + -108, + -96, + -84, + -72, + -66, + -60, + -54, + -54, + -54, + -60, + -60, + -66, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -60, + -60, + -60, + -60, + -66, + -66, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -66, + -66, + -66, + -72, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36,-36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 30, + 30, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 66, + 66, + 72, + 72, + 84, + 84, + 84, + 84, + 84, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 36, + 36, + 30, + 30, + 30, + 30, + 42, + 48, + 60, + 66, + 78, + 78, + 84, + 90, + 96, + 114, + 138, + 168, + 210, + 258, + 312, + 366, + 408, + 438, + 450, + 438, + 402, + 348, + 282, + 204, + 120, + 42, + -24, + -72, + -102, + -108, + -102, + -78, + -48, + -12, + 24, + 54, + 78, + 90, + 96, + 96, + 96, + 84, + 90, + 90, + 90, + 84, + 78, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -12, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -24, + -30, + -30, + -36, + -36, + -42, + -48, + -48, + -54, + -54, + -60, + -60, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -90, + -90, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -66, + -60, + -60, + -60, + -60, + -60, + -66, + -72, + -78, + -78, + -72, + -54, + -24, + 12, + 66, + 126, + 240, + 282, + 300, + 300, + 270, + 216, + 144, + 60, + -30, + -120, + -192, + -240, + -276, + -282, + -270, + -246, + -210, + -168, + -132, + -108, + -84, + -72, + -66, + -72, + -72, + -78, + -72, + -72, + -72, + -66, + -66, + -60, + -54, + -54, + -48, + -42, + -42, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -24, + -30, + -30, + -36, + -36, + -42, + -42, + -48, + -48, + -48, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -90, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -66, + -66, + -66, + -60, + -60, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -66, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -72, + -66, + -66, + -66, + -72, + -78, + -78, + -78, + -78, + -72, + -54, + -42, + -24, + -6, + 12, + 30, + 42, + 60, + 78, + 102, + 132, + 168, + 216, + 264, + 306, + 348, + 372, + 372, + 354, + 312, + 246, + 162, + 72, + -18, + -96, + -162, + -198, + -216, + -216, + -192, + -162, + -120, + -84, + -54, + -30, + -18, + -12, + -12, + -18, + -12, + -6, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 18, + 18, + 24, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 90, + 90, + 90, + 96, + 96, + 102, + 102, + 108, + 108, + 114, + 114, + 114, + 120, + 120, + 120, + 126, + 126, + 126, + 126, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 102, + 102, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 78, + 72, + 72, + 72, + 66, + 78, + 78, + 78, + 72, + 72, + 78, + 78, + 60, + 66, + 66, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 96, + 96, + 102, + 114, + 126, + 144, + 174, + 204, + 288, + 330, + 366, + 390, + 402, + 390, + 360, + 306, + 234, + 156, + 72, + -12, + -78, + -132, + -168, + -180, + -174, + -150, + -120, + -84, + -42, + -12, + 12, + 30, + 42, + 48, + 48, + 48, + 54, + 54, + 60, + 60, + 66, + 72, + 78, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 120, + 120, + 120, + 120, + 126, + 126, + 126, + 126, + 126, + 132, + 132, + 132, + 132, + 132, + 132, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 102, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 78, + 72, + 72, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -30, + -30, + -36, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -54, + -60, + -60, + -60, + -54, + -48, + -36, + -24, + -18, + -6, + -6, + 0, + 6, + 18, + 36, + 60, + 96, + 138, + 192, + 240, + 282, + 312, + 318, + 306, + 264, + 204, + 132, + 42, + -42, + -120, + -186, + -228, + -252, + -252, + -234, + -204, + -162, + -126, + -96, + -66, + -48, + -36, + -30, + -30, + -30, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + 0, + 0, + 0, + 0, + -6, + -12, + -18, + -24, + -24, + -24, + -18, + -12, + -6, + 0, + 12, + 24, + 36, + 54, + 78, + 108, + 144, + 186, + 228, + 276, + 312, + 342, + 354, + 342, + 318, + 270, + 204, + 132, + 54, + -18, + -84, + -126, + -162, + -150, + -132, + -108, + -78, + -54, + -36, + -18, + -12, + -6, + 0, + 6, + 12, + 18, + 12, + 12, + 6, + 0, + 0, + -6, + -12, + -12, + -18, + -18, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 12, + 12, + 12, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 42, + 54, + 78, + 114, + 156, + 204, + 264, + 324, + 384, + 432, + 456, + 468, + 450, + 402, + 342, + 258, + 168, + 84, + 6, + -54, + -90, + -108, + -102, + -78, + -42, + -12, + 24, + 48, + 66, + 72, + 72, + 60, + 54, + 42, + 42, + 54, + 48, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 60, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 0, + 6, + 6, + 0, + 0, + -6, + -12, + -18, + -24, + -30, + -36, + -36, + -42, + -48, + -48, + -54, + -60, + -60, + -66, + -66, + -72, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -108, + -108, + -108, + -108, + -108, + -108, + -102, + -102, + -102, + -96, + -96, + -96, + -90, + -90, + -90, + -84, + -84, + -78, + -78, + -78, + -72, + -72, + -66, + -66, + -60, + -60, + -54, + -54, + -48, + -48, + -42, + -42, + -42, + -36, + -36, + -36, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -30, + -30, + -30, + -36, + -36, + -30, + -36, + -30, + -30, + -24, + -24, + -30, + -36, + -42, + -48, + -54, + -48, + -42, + -30, + -18, + -12, + 0, + 12, + 18, + 30, + 48, + 72, + 102, + 144, + 192, + 246, + 294, + 330, + 354, + 354, + 336, + 294, + 234, + 156, + 72, + -6, + -84, + -138, + -180, + -198, + -192, + -180, + -114, + -84, + -54, + -30, + -12, + 0, + 6, + 6, + 0, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 24, + 18, + 12, + 12, + 6, + 6, + 6, + 12, + 18, + 30, + 36, + 48, + 60, + 84, + 114, + 150, + 192, + 240, + 294, + 336, + 378, + 396, + 390, + 366, + 318, + 246, + 162, + 78, + -6, + -78, + -132, + -162, + -168, + -156, + -132, + -96, + -60, + -24, + 0, + 18, + 24, + 30, + 30, + 24, + 24, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -54, + -54, + -54, + -60, + -60,-60, + -60, + -66, + -66, + -72, + -72, + -72, + -72, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -60, + -54, + -36, + -12, + 18, + 60, + 102, + 156, + 204, + 252, + 288, + 312, + 312, + 288, + 246, + 180, + 102, + 18, + -66, + -144, + -204, + -246, + -258, + -240, + -210, + -174, + -138, + -108, + -84, + -72, + -66, + -66, + -72, + -78, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18,-18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 12, + 6, + 6, + 6, + 12, + 18, + 24, + 36, + 48, + 66, + 84, + 114, + 150, + 186, + 234, + 282, + 324, + 354, + 372, + 372, + 342, + 294, + 228, + 144, + 60, + -24, + -96, + -144, + -174, + -180, + -168, + -138, + -102, + -60, + -30, + 0, + 18, + 24, + 24, + 24, + 18, + 18, + 24, + 24, + 24, + 18, + 18, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 60, + 66, + 66, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 48, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 30, + 30, + 42, + 36, + 30, + 30, + 18, + 0, + -12, + -24, + -18, + -12, + 12, + 30, + 48, + 54, + 42, + 18, + -30, + -90, + -156, + -222, + -282, + -324, + -348, + -348, + -324, + -282, + -234, + -174, + -114, + -66, + -24, + 0, + 24, + 30, + 42, + 48, + 54, + 72, + 96, + 126, + 168, + 216, + 264, + 318, + 366, + 402, + 426, + 432, + 420, + 384, + 324, + 252, + 168, + 78, + -6, + -72, + -114, + -138, + -138, + -114, + -78, + -36, + 6, + 66, + 84, + 84, + 78, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -60, + -60, + -60, + -66, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -96, + -96, + -84, + -90, + -90, + -96, + -96, + -96, + -96, + -102, + -108, + -108, + -114, + -114, + -114, + -108, + -108, + -102, + -96, + -84, + -78, + -42, + -12, + 24, + 66, + 114, + 168, + 216, + 252, + 270, + 264, + 234, + 180, + 108, + 18, + -72, + -156, + -228, + -288, + -318, + -324, + -312, + -282, + -246, + -198, + -162, + -126, + -102, + -84, + -78, + -72, + -66, + -66, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 6, + 6, + 12, + 12, + 18, + 24, + 24, + 30, + 30, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + 0, + 6, + 6, + 12, + 12, + 12, + 12, + 6, + 6, + 12, + 24, + 36, + 60, + 96, + 132, + 174, + 216, + 252, + 288, + 306, + 312, + 294, + 264, + 216, + 156, + 78, + 6, + -72, + -138, + -186, + -222, + -240, + -234, + -216, + -180, + -144, + -102, + -66, + -36, + -18, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + 6, + 6, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 36, + 36, + 36, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 84, + 90, + 90, + 90, + 96, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 24, + 18, + 18, + 24, + 24, + 30, + 30, + 24, + 12, + -12, + -48, + -90, + -144, + -204, + -252, + -276, + -282, + -270, + -228, + -174, + -114, + -60, + -30, + 0, + 18, + 24, + 24, + 18, + 18, + 12, + 12, + 18, + 24, + 36, + 42, + 48, + 42, + 36, + 36, + 30, + 36, + 36, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 66, + 66, + 72, + 72, + 78, + 78, + 84, + 84, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 30, + 48, + 54, + 48, + 42, + 36, + 18, + -6, + -18, + -30, + -36, + -36, + -24, + -12, + 6, + 30, + 48, + 66, + 84, + 102, + 126, + 150, + 186, + 228, + 270, + 312, + 342, + 360, + 360, + 330, + 276, + 198, + 108, + 12, + -78, + -156, + -210, + -234, + -234, + -216, + -180, + -132, + -90, + -60, + -30, + -30, + -36, + -48, + -54, + -54, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -48, + -36, + -36, + -36, + -30, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -18, + -12, + 6, + 6, + 0, + -18, + -36, + -54, + -66, + -60, + -48, + -24, + 6, + 30, + 48, + 54, + 30, + -18, + -90, + -180, + -282, + -378, + -462, + -522, + -558, + -570, + -552, + -468, + -420, + -372, + -330, + -294, + -270, + -246, + -228, + -210, + -186, + -162, + -138, + -114, + -96, + -84, + -72, + -72, + -78, + -84, + -96, + -96, + -96, + -84, + -72, + -48, + -18, + 6, + 24, + 30, + 30, + 24, + 18, + 6, + -6, + -24, + -36, + -48, + -60, + -72, + -72, + -72, + -42, + -42, + -30, + -30, + -24, + -24, + -24, + -24, + -12, + -6, + -6, + 0, + 0, + 6, + 0, + 6, + 6, + 12, + 18, + 18, + 12, + 18, + 30, + 42, + 54, + 60, + 72, + 84, + 96, + 108, + 114, + 120, + 132, + 138, + 144, + 156, + 156, + 162, + 168, + 168, + 168, + 162, + 150, + 138, + 126, + 108, + 96, + 84, + 66, + 54, + 48, + 42, + 36, + 42, + 48, + 60, + 78, + 90, + 108, + 114, + 126, + 126, + 132, + 132, + 126, + 132, + 120, + 114, + 108, + 96, + 90, + 90, + 96, + 96, + 96, + 90, + 72, + 54, + 36, + 12, + -12, + -30, + -48, + -66, + -78, + -78, + -78, + -72, + -66, + -54, + -48, + -42, + -48, + -42, + -30, + -30, + -24, + -18, + -12, + -6, + 0, + 12, + 24, + 36, + 48, + 60, + 66, + 78, + 84, + 90, + 96, + 102, + 102, + 108, + 102, + 102, + 108, + 108, + 114, + 114, + 102, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 102, + 102, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 72, + 72, + 66, + 60, + 54, + 54, + 48, + 42, + 36, + 30, + 30, + 24, + 18, + 6, + 0, + -6, + -12, + -24, + -30, + -36, + -48, + -54, + -60, + -66, + -72, + -84, + -96, + -66, + -78, + -96, + -114, + -132, + -150, + -162, + -174, + -180, + -180, + -186, + -180, + -186, + -162, + -138, + -108, + -84, + -60, + -42, + -24, + -12, + -12, + -36, + -72, + -126, + -186, + -258, + -324, + -390, + -438, + -462, + -468, + -450, + -408, + -348, + -288, + -222, + -162, + -114, + -78, + -60, + -54, + -60, + -72, + -84, + -102, + -108, + -108, + -102, + -90, + -66, + -36, + -6, + 24, + 54, + 72, + 90, + 90, + 72, + 60, + 48, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 60, + 60, + 66, + 72, + 72, + 78, + 78, + 84, + 84, + 90, + 90, + 96, + 108, + 108, + 108, + 114, + 114, + 114, + 120, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 102, + 102, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 90, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 90, + 90, + 90, + 96, + 102, + 114, + 120, + 120, + 114, + 96, + 72, + 42, + -12, + -66, + -126, + -180, + -216, + -234, + -234, + -210, + -174, + -78, + -30, + 12, + 42, + 60, + 72, + 72, + 66, + 60, + 54, + 48, + 42, + 42, + 36, + 30, + 30, + 24, + 30, + 36, + 48, + 60, + 72, + 72, + 54, + 48, + 36, + 18, + 6, + -12, + -30, + -48, + -60, + -66, + -66, + -42, + -36, + -12, + 6, + 18, + 30, + 30, + 42, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 60, + 54, + 48, + 42, + 30, + 18, + 12, + 6, + 18, + 30, + 42, + 48, + 42, + 24, + 0, + -36, + -84, + -132, + -180, + -222, + -258, + -288, + -306, + -318, + -324, + -324, + -318, + -312, + -306, + -300, + -294, + -288, + -276, + -270, + -258, + -252, + -246, + -240, + -234, + -222, + -216, + -210, + -198, + -192, + -186, + -180, + -174, + -168, + -168, + -162, + -150, + -150, + -144, + -138, + -126, + -120, + -108, + -102, + -96, + -90, + -84, + -78, + -72, + -66, + -60, + -54, + -48, + -42, + -36, + -30, + -30, + -24, + -18, + -12, + -6, + 0, + 6, + 12, + 18, + 24, + 30, + 18, + 48, + 60, + 66, + 78, + 84, + 96, + 102, + 114, + 120, + 138, + 150, + 162, + 180, + 186, + 192, + 198, + 204, + 204, + 210, + 216, + 216, + 222, + 222, + 228, + 228, + 228, + 228, + 234, + 234, + 228, + 228, + 228, + 222, + 222, + 222, + 222, + 228, + 240, + 252, + 276, + 300, + 336, + 384, + 432, + 486, + 540, + 588, + 624, + 636, + 630, + 594, + 534, + 456, + 366, + 270, + 180, + 108, + 60, + 36, + 36, + 54, + 84, + 120, + 156, + 186, + 198, + 204, + 198, + 192, + 180, + 168, + 156, + 162, + 168, + 162, + 162, + 156, + 156, + 156, + 156, + 156, + 156, + 150, + 150, + 138, + 132, + 126, + 120, + 108, + 102, + 96, + 84, + 84, + 78, + 90, + 102, + 120, + 132, + 144, + 150, + 150, + 150, + 138,126, + 120, + 120, + 132, + 150, + 168, + 186, + 198, + 198, + 186, + 156, + 114, + 66, + 18, + -30, + -72, + -108, + -138, + -156, + -174, + -192, + -204, + -222, + -246, + -264, + -282, + -294, + -300, + -300, + -294, + -282, + -258, + -240, + -216, + -192, + -168, + -144, + -126, + -102, + -84, + -72, + -54, + -42, + -36, + -24, + -24, + -18, + -18, + -12, + -18, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -36, + -36, + -42, + -48, + -54, + -54, + -48, + -42, + -24, + -6, + 12, + 30, + 48, + 72, + 90, + 108, + 120, + 138, + 144, + 156, + 156, + 162, + 162, + 168, + 174, + 180, + 186, + 192, + 192, + 186, + 168, + 132, + 84, + 24, + -42, + -114, + -240, + -288, + -318, + -336, + -342, + -336, + -318, + -306, + -288, + -276, + -270, + -264, + -258, + -252, + -246, + -228, + -210, + -186, + -168, + -150, + -132, + -114, + -108, + -102, + -96, + -96, + -96, + -90, + -78, + -66, + -48, + -30, + -6, + 18, + 42, + 66, + 90, + 108, + 126, + 144, + 162, + 174, + 186, + 198, + 204, + 210, + 216, + 222, + 228, + 228, + 228, + 228, + 228, + 228, + 228, + 234, + 234, + 234, + 234, + 234, + 228, + 228, + 228, + 222, + 222, + 222, + 216, + 216, + 216, + 210, + 210, + 210, + 204, + 204, + 204, + 204, + 198, + 198, + 198, + 192, + 192, + 174, + 144, + 138, + 138, + 132, + 126, + 126, + 138, + 156, + 174, + 198, + 216, + 228, + 228, + 210, + 168, + 108, + 36, + -48, + -132, + -210, + -270, + -306, + -318, + -306, + -276, + -228, + -174, + -114, + -66, + -24, + 0, + 18, + 30, + 36, + 42, + 42, + 48, + 54, + 72, + 72, + 72, + 72, + 66, + 66, + 60, + 60, + 66, + 72, + 78, + 90, + 96, + 96, + 96, + 90, + 84, + 72, + 54, + 42, + 36, + 24, + 24, + 18, + 24, + 30, + 48, + 78, + 114, + 162, + 222, + 282, + 348, + 402, + 444, + 456, + 444, + 396, + 324, + 222, + 102, + -24, + -150, + -252, + -330, + -378, + -390, + -366, + -318, + -252, + -174, + -96, + -30, + 18, + 54, + 72, + 72, + 60, + 24, + 12, + 12, + 18, + 36, + 60, + 78, + 96, + 96, + 84, + 54, + 6, + -66, + -144, + -234, + -324, + -408, + -474, + -528, + -558, + -570, + -558, + -534, + -504, + -468, + -438, + -408, + -390, + -378, + -378, + -378, + -378, + -372, + -354, + -324, + -282, + -228, + -162, + -96, + -36, + 12, + 48, + 60, + 54, + 30, + -12, + -54, + -90, + -102, + -72, + -66, + 0, + 6, + 12, + 18, + 30, + 36, + 42, + 54, + 66, + 72, + 78, + 90, + 96, + 102, + 114, + 120, + 126, + 138, + 144, + 156, + 162, + 168, + 180, + 186, + 192, + 198, + 186, + 192, + 192, + 216, + 222, + 222, + 228, + 228, + 228, + 228, + 228, + 228, + 228, + 222, + 222, + 216, + 210, + 204, + 198, + 192, + 186, + 180, + 174, + 168, + 162, + 156, + 150, + 144, + 138, + 132, + 126, + 120, + 114, + 108, + 102, + 96, + 90, + 84, + 78, + 78, + 66, + 60, + 54, + 48, + 42, + 42, + 36, + 30, + 24, + 18, + 6, + 0, + -6, + -12, + -18, + -24, + -30, + -36, + -42, + -48, + -54, + -60, + -78, + -84, + -84, + -72, + -78, + -84, + -90, + -90, + -90, + -90, + -90, + -84, + -72, + -66, + -54, + -42, + -30, + -36, + -12, + -12, + -6, + -6, + -6, + -12, + -6, + -6, + -6, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -24, + -18, + -18, + -12, + -6, + 0, + 6, + 12, + 12, + 6, + -6, + -30, + -54, + -78, + -102, + -132, + -150, + -156, + -168, + -174, + -174, + -168, + -138, + -126, + -108, + -96, + -90, + -78, + -90, + -78, + -72, + -66, + -66, + -60, + -60, + -66, + -66, + -72, + -72, + -78, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -60, + -60, + -54, + -48, + -48, + -42, + -36, + -36, + -30, + -24, + -24, + -18, + -18, + -12, + -12, + 6, + 6, + 0, + 6, + 0, + 0, + -6, + -12, + -18, + -24, + -30, + -36, + -42, + -48, + -54, + -54, + -48, + -48, + -42, + -36, + -30, + -24, + -24, + -30, + -36, + -42, + -42, + -42, + -30, + -18, + 6, + 30, + 54, + 84, + 108, + 126, + 150, + 168, + 192, + 216, + 246, + 288, + 330, + 378, + 414, + 444, + 438, + 402, + 342, + 264, + 174, + 90, + 12, + -42, + -78, + -90, + -78, + -54, + -12, + 24, + 60, + 84, + 96, + 102, + 96, + 90, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 78, + 66, + 54, + 48, + 48, + 54, + 66, + 78, + 72, + 54, + 24, + -18, + -54, + -132, + -210, + -282, + -336, + -366, + -372, + -348, + -312, + -258, + -216, + -180, + -162, + -174, + -234, + -276, + -330, + -378, + -468, + -492, + -492, + -486, + -468, + -438, + -402, + -372, + -342, + -324, + -306, + -288, + -270, + -246, + -222, + -198, + -168, + -144, + -120, + -108, + -96, + -90, + -84, + -78, + -66, + -54, + -30, + -6, + 18, + 36, + 54, + 60, + 78, + 78, + 84, + 78, + 90, + 96, + 102, + 108, + 108, + 114, + 114, + 120, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 114, + 108, + 102, + 96, + 90, + 84, + 66, + 60, + 48, + 42, + 36, + 24, + 18, + 12, + 6, + 6, + 6, + 18, + 24, + 36, + 54, + 66, + 84, + 96, + 108, + 114, + 120, + 126, + 132, + 132, + 138, + 138, + 138, + 144, + 144, + 150, + 150, + 156, + 156, + 156, + 156, + 138, + 126, + 108, + 90, + 66, + 48, + 24, + 6, + -12, + -24, + -30, + -24, + -24, + -18, + -6, + 0, + 6, + 12, + 18, + 30, + 36, + 48, + 60, + 66, + 72, + 78, + 84, + 84, + 84, + 78, + 72, + 66, + 66, + 72, + 84, + 90, + 90, + 78, + 72, + 60, + 42, + 12, + 0, + -6, + -18, + -24, + -30, + -36, + -42, + -48, + -36, + -36, + -42, + -48, + -48, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -66, + -66, + -66, + -60, + -66, + -72, + -84, + -102, + -120, + -132, + -132, + -126, + -108, + -78, + -48, + -12, + 12, + 36, + 54, + 54, + 54, + 48, + 48, + 48, + 54, + 66, + 78, + 84, + 84, + 72, + 48, + 6, + -48, + -120, + -192, + -270, + -348, + -414, + -462, + -504, + -528, + -534, + -528, + -516, + -498, + -474, + -450, + -426, + -408, + -396, + -390, + -384, + -378, + -366, + -360, + -348, + -342, + -330, + -318, + -312, + -300, + -288, + -282, + -276, + -264, + -258, + -252, + -240, + -222, + -204, + -186, + -156, + -132, + -96, + -66, + -30, + -6, + 36, + 66, + 96, + 114, + 138, + 144, + 144, + 156, + 162, + 162, + 168, + 174, + 180, + 186, + 198, + 210, + 222, + 240, + 264, + 282, + 324, + 372, + 426, + 480, + 522, + 552, + 558, + 546, + 504, + 438, + 360, + 276, + 192, + 120, + 60, + 30, + 24, + 36, + 72, + 108, + 156, + 192, + 222, + 240, + 246, + 240, + 228, + 216, + 216, + 216, + 222, + 222, + 228, + 234, + 240, + 246, + 252, + 252, + 246, + 240, + 240, + 234, + 222, + 216, + 204, + 198, + 198, + 192, + 186, + 180, + 180, + 174, + 168, + 162, + 162, + 156, + 150, + 150, + 156, + 150, + 150, + 144, + 144, + 144, + 144, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 102, + 102, + 96, + 96, + 90, + 84, + 84, + 90, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 60, + 54, + 48, + 42, + 30, + 24, + 6, + 0, + -12, + -30, + -48, + -66, + -84, + -108, + -132, + -150, + -168, + -186, + -186, + -186, + -174, + -162, + -150, + -132, + -126, + -114, + -108, + -102, + -96, + -90, + -84, + -72, + -66, + -54, + -48, + -42, + -42, + -42, + -60, + -78, + -90, + -108, + -120, + -120, + -126, + -132, + -138, + -144, + -144, + -150, + -150, + -150, + -144, + -132, + -132, + -132, + -126, + -126, + -114, + -114, + -108, + -108, + -108, + -102, + -102, + -96, + -96, + -90, + -84, + -84, + -84, + -78, + -72, + -66, + -66, + -60, + -54, + -48, + -48, + -42, + -36, + -36, + -30, + -30, + -24, + -24, + -18, + -6, + -6, + -6, + -6, + -12, + -18, + -24, + -30, + -36, + -36, + -42, + -48, + -54, + -54, + -60, + -60, + -66, + -66, + -66, + -60, + -60, + -54, + -48, + -42, + -30, + -24, + -12, + 0, + 6, + 18, + 24, + 30, + 36, + 36, + 36, + 42, + 48, + 60, + 66, + 66, + 60, + 48, + 18, + -12, + -48, + -84, + -120, + -150, + -168, + -180, + -180, + -180, + -174, + -174, + -174, + -180, + -192, + -204, + -210, + -222, + -210, + -198, + -180, + -156, + -138, + -120, + -96, + -72, + -48, + -24, + -6, + 6, + 24, + 30, + 30, + 12, + 6, + -12, + -24, + -36, + -48, + -48, + -48, + -48, + -48, + -42, + -48, + -24, + 6, + 36, + 54, + 66, + 72, + 72, + 72, + 66, + 66, + 60, + 60, + 66, + 72, + 84, + 96, + 114, + 138, + 168, + 210, + 258, + 312, + 366, + 420, + 456, + 474, + 468, + 444, + 390, + 318, + 234, + 144, + -18, + -72, + -114, + -132, + -126, + -114, + -84, + -54, + -24, + 0, + 18, + 36, + 42, + 48, + 54, + 54, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 48, + 48, + 42, + 42, + 36, + 30, + 24, + 24, + 18, + 12, + 6, + 0, + -6, + -12, + -18, + -24, + -30, + -36, + -42, + -42, + -48, + -54, + -60, + -60, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -48, + -48, + -48, + -42, + -48, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -36, + -36, + -30, + -30, + -30, + -36, + -42, + -54, + -84, + -114, + -138, + -156, + -180, + -228, + -252, + -264, + -270, + -264, + -252, + -234, + -210, + -186, + -162, + -156, + -162, + -162, + -162, + -162, + -156, + -150, + -138, + -138, + -150, + -168, + -204, + -258, + -318, + -378, + -444, + -498, + -540, + -570, + -588, + -594, + -588, + -576, + -558, + -528, + -492, + -450, + -396, + -330, + -258, + -192, + -132, + -84, + -54, + -54, + -78, + -126, + -180, + -246, + -306, + -324, + -336, + -324,-270, + -240, + -204, + -162, + -126, + -96, + -66, + -48, + -30, + -18, + -12, + 6, + 12, + 18, + 24, + 30, + 42, + 48, + 54, + 60, + 60, + 66, + 66, + 72, + 78, + 84, + 90, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 78, + 72, + 66, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 72, + 72, + 66, + 72, + 78, + 84, + 84, + 78, + 72, + 72, + 72, + 72, + 78, + 84, + 90, + 102, + 114, + 126, + 144, + 174, + 204, + 246, + 288, + 336, + 384, + 420, + 444, + 438, + 414, + 366, + 294, + 210, + 120, + 30, + -48, + -108, + -144, + -162, + -162, + -150, + -126, + -102, + -78, + -60, + -42, + -30, + -18, + 0, + 18, + 36, + 48, + 48, + 42, + 24, + 6, + -30, + -60, + -90, + -144, + -156, + -162, + -156, + -144, + -120, + -90, + -60, + -42, + -30, + -24, + -30, + -48, + -78, + -108, + -150, + -192, + -246, + -300, + -360, + -420, + -480, + -528, + -570, + -594, + -606, + -606, + -588, + -564, + -540, + -510, + -486, + -468, + -456, + -450, + -444, + -438, + -420, + -408, + -390, + -372, + -354, + -336, + -318, + -306, + -294, + -282, + -288, + -294, + -294, + -294, + -288, + -264, + -240, + -204, + -162, + -114, + -66, + -24, + 18, + 54, + 84, + 138, + 156, + 174, + 192, + 204, + 216, + 222, + 222, + 216, + 210, + 210, + 204, + 204, + 216, + 222, + 228, + 240, + 246, + 252, + 258, + 264, + 258, + 240, + 216, + 186, + 156, + 126, + 96, + 72, + 48, + 30, + 18, + 0, + -18, + -36, + -54, + -72, + -96, + -114, + -132, + -144, + -162, + -174, + -180, + -192, + -204, + -216, + -222, + -228, + -228, + -228, + -222, + -216, + -210, + -210, + -204, + -204, + -198, + -186, + -180, + -174, + -162, + -156, + -138, + -132, + -120, + -114, + -114, + -102, + -96, + -90, + -84, + -72, + -66, + -48, + -30, + -6, + 24, + 54, + 84, + 114, + 138, + 162, + 186, + 204, + 216, + 228, + 234, + 246, + 246, + 246, + 252, + 258, + 258, + 258, + 258, + 264, + 270, + 270, + 258, + 258, + 264, + 270, + 270, + 270, + 276, + 276, + 276, + 276, + 276, + 276, + 288, + 288, + 288, + 294, + 294, + 294, + 294, + 294, + 294, + 294, + 288, + 276, + 270, + 276, + 282, + 276, + 258, + 240, + 222, + 210, + 210, + 222, + 240, + 270, + 294, + 306, + 300, + 270, + 210, + 132, + 42, + -54, + -150, + -222, + -270, + -282, + -270, + -228, + -174, + -108, + -48, + 0, + 36, + 54, + 60, + 54, + 54, + 54, + 66, + 84, + 114, + 150, + 186, + 216, + 234, + 246, + 246, + 240, + 228, + 216, + 210, + 228, + 234, + 246, + 246, + 246, + 246, + 240, + 240, + 240, + 234, + 234, + 228, + 222, + 222, + 216, + 210, + 204, + 198, + 192, + 180, + 180, + 180, + 174, + 162, + 156, + 150, + 150, + 144, + 150, + 150, + 144, + 144, + 138, + 138, + 138, + 138, + 138, + 138, + 150, + 150, + 156, + 156, + 156, + 150, + 144, + 138, + 138, + 144, + 144, + 156, + 162, + 174, + 180, + 180, + 186, + 192, + 198, + 222, + 252, + 288, + 342, + 396, + 504, + 534, + 546, + 528, + 480, + 402, + 312, + 204, + 102, + 12, + -60, + -108, + -132, + -126, + -102, + -66, + -24, + 18, + 48, + 66, + 78, + 78, + 72, + 66, + 66, + 66, + 66, + 78, + 84, + 90, + 90, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 102, + 108, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 102, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 90, + 90, + 90, + 84, + 84, + 84, + 78, + 78, + 78, + 72, + 72, + 66, + 66, + 60, + 60, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -12, + -18, + -18, + -24, + -18, + -24, + -24, + -30, + -30, + -36, + -42, + -48, + -54, + -60, + -72, + -84, + -96, + -102, + -114, + -126, + -138, + -144, + -150, + -156, + -162, + -162, + -156, + -150, + -144, + -138, + -126, + -120, + -114, + -102, + -96, + -90, + -78, + -72, + -72, + -66, + -60, + -54, + -48, + -42, + -42, + -36, + -42, + -42, + -36, + -36, + -30, + -42, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 6, + 0, + 0, + -6, + -12, + -12, + -18, + -12, + -18, + -18, + -24, + -24, + -30, + -36, + -36, + -42, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -30, + -24, + -12, + 6, + 24, + 54, + 90, + 132, + 186, + 240, + 288, + 330, + 360, + 366, + 354, + 312, + 252, + 174, + 90, + 6, + -72, + -138, + -180, + -198, + -198, + -180, + -150, + -114, + -84, + -54, + -36, + -30, + -24, + -24, + -30, + -18, + -12, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -12, + -6, + -6, + -6, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -12, + -12, + -12, + -18, + -24, + -24, + -30, + -36, + -36, + -42, + -48, + -54, + -54, + -60, + -66, + -66, + -72, + -84, + -84, + -90, + -90, + -78, + -84, + -84, + -84, + -84, + -84, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -54, + -60, + -54, + -54, + -54, + -60, + -54, + -48, + -48, + -36, + -30, + -18, + -6, + 0, + 6, + 12, + 18, + 24, + 24, + 24, + 24, + 18, + 12, + 6, + -6, + -6, + -6, + 6, + 18, + 42, + 60, + 78, + 78, + 66, + 24, + -42, + -126, + -228, + -342, + -456, + -552, + -636, + -690, + -720, + -726, + -714, + -690, + -666, + -642, + -630, + -624, + -630, + -630, + -630, + -618, + -552, + -492, + -420, + -348, + -276, + -216, + -168, + -138, + -120, + -120, + -126, + -126, + -126, + -120, + -102, + -78, + -48, + -12, + 6, + 12, + 24, + 30, + 36, + 36, + 42, + 48, + 54, + 60, + 66, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 84, + 90, + 90, + 96, + 96, + 102, + 102, + 108, + 114, + 114, + 120, + 126, + 126, + 132, + 132, + 138, + 138, + 144, + 144, + 144, + 150, + 150, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 156, + 150, + 150, + 150, + 150, + 150, + 150, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 102, + 102, + 96, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 12, + 12, + 12, + 12, + 18, + 12, + 18, + 18, + 12, + 12, + 12, + 18, + 18, + 24, + 24, + 24, + 18, + 18, + 12, + 12, + 24, + 42, + 120, + 174, + 234, + 294, + 342, + 372,384, + 366, + 324, + 264, + 186, + 96, + 6, + -72, + -132, + -180, + -198, + -198, + -186, + -162, + -132, + -102, + -78, + -60, + -48, + -42, + -42, + -48, + -42, + -30, + -24, + -18, + -12, + -12, + -12, + -12, + -24, + -24, + -30, + -36, + -42, + -48, + -48, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -30, + -30, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -24, + -30, + -30, + -36, + -42, + -48, + -48, + -54, + -54, + -54, + -60, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -72, + -90, + -114, + -156, + -258, + -324, + -384, + -450, + -510, + -558, + -594, + -624, + -636, + -648, + -654, + -654, + -654, + -654, + -654, + -654, + -654, + -648, + -642, + -636, + -630, + -618, + -612, + -600, + -594, + -588, + -576, + -570, + -564, + -558, + -546, + -540, + -540, + -534, + -522, + -522, + -516, + -510, + -510, + -510, + -504, + -498, + -486, + -462, + -426, + -390, + -342, + -288, + -234, + -180, + -138, + -96, + -72, + -48, + -42, + -36, + -42, + -48, + -54, + -54, + -48, + -48, + -42, + -30, + -18, + -12, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 24, + 24, + 24, + 12, + 0, + -6, + -12, + -18, + -24, + -24, + -24, + -18, + -12, + -6, + 0, + 12, + 18, + 24, + 30, + 36, + 48, + 60, + 66, + 66, + 54, + 36, + 6, + -42, + -90, + -150, + -210, + -264, + -354, + -390, + -408, + -420, + -432, + -432, + -438, + -444, + -450, + -450, + -456, + -456, + -450, + -444, + -432, + -408, + -396, + -384, + -366, + -354, + -348, + -342, + -336, + -336, + -330, + -330, + -324, + -324, + -318, + -306, + -300, + -294, + -288, + -288, + -282, + -276, + -282, + -276, + -270, + -270, + -264, + -264, + -258, + -252, + -252, + -246, + -246, + -246, + -240, + -240, + -234, + -234, + -228, + -228, + -228, + -222, + -222, + -222, + -216, + -216, + -216, + -210, + -204, + -204, + -204, + -198, + -198, + -192, + -186, + -186, + -198, + -192, + -186, + -180, + -174, + -168, + -162, + -162, + -162, + -156, + -156, + -150, + -150, + -150, + -144, + -138, + -126, + -108, + -84, + -60, + -30, + 12, + 60, + 108, + 150, + 192, + 222, + 234, + 222, + 198, + 150, + 84, + 6, + -78, + -162, + -228, + -276, + -300, + -300, + -282, + -240, + -198, + -156, + -114, + -90, + -72, + -72, + -78, + -84, + -90, + -48, + -6, + 54, + 120, + 198, + 270, + 336, + 390, + 426, + 450, + 456, + 450, + 438, + 426, + 408, + 402, + 396, + 390, + 390, + 396, + 396, + 396, + 390, + 378, + 360, + 336, + 306, + 270, + 234, + 192, + 150, + 114, + 84, + 54, + 36, + 30, + 24, + 30, + 36, + 36, + 42, + 42, + 48, + 54, + 54, + 48, + 48, + 48, + 72, + 72, + 72, + 72, + 72, + 78, + 78, + 84, + 84, + 72, + 72, + 84, + 84, + 84, + 90, + 96, + 102, + 108, + 114, + 126, + 138, + 144, + 162, + 180, + 198, + 216, + 240, + 258, + 276, + 294, + 306, + 318, + 324, + 330, + 330, + 336, + 336, + 336, + 342, + 342, + 342, + 342, + 336, + 336, + 336, + 336, + 330, + 330, + 324, + 324, + 318, + 318, + 312, + 306, + 306, + 300, + 294, + 294, + 288, + 282, + 276, + 276, + 270, + 270, + 264, + 264, + 258, + 252, + 252, + 246, + 240, + 240, + 234, + 234, + 228, + 228, + 222, + 222, + 216, + 216, + 210, + 210, + 204, + 204, + 204, + 198, + 198, + 192, + 192, + 192, + 186, + 186, + 186, + 180, + 180, + 180, + 174, + 174, + 174, + 174, + 168, + 168, + 168, + 162, + 162, + 162, + 156, + 156, + 156, + 150, + 150, + 150, + 144, + 144, + 138, + 138, + 138, + 132, + 132, + 132, + 126, + 126, + 120, + 120, + 120, + 114, + 114, + 108, + 108, + 102, + 102, + 102, + 96, + 96, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -18, + -12, + -18, + -18, + -24, + -30, + -30, + -24, + -12, + 0, + 18, + 42, + 66, + 96, + 126, + 156, + 192, + 222, + 252, + 270, + 282, + 276, + 258, + 216, + 156, + 84, + 6, + -72, + -144, + -198, + -246, + -240, + -216, + -186, + -150, + -120, + -96, + -78, + -78, + -78, + -84, + -84, + -72, + -54, + -18, + 24, + 78, + 126, + 174, + 216, + 252, + 270, + 276, + 282, + 276, + 270, + 264, + 264, + 264, + 270, + 264, + 258, + 258, + 258, + 252, + 252, + 252, + 246, + 240, + 240, + 234, + 234, + 228, + 222, + 216, + 216, + 210, + 210, + 204, + 198, + 198, + 192, + 192, + 186, + 180, + 180, + 174, + 174, + 186, + 186, + 180, + 180, + 156, + 132, + 102, + 72, + 42, + 18, + 0, + -6, + -12, + -18, + -24, + -36, + -54, + -84, + -120, + -168, + -216, + -258, + -288, + -312, + -318, + -306, + -276, + -234, + -192, + -144, + -96, + -54, + -18, + 6, + 30, + 42, + 54, + 66, + 78, + 84, + 90, + 90, + 90, + 84, + 72, + 66, + 66, + 66, + 72, + 78, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 24, + 24, + 18, + 18, + 12, + 12, + 6, + 6, + 0, + 0, + -6, + -6, + -12, + -12, + -18, + -24, + -24, + -30, + -30, + -36, + -36, + -42, + -42, + -48, + -54, + -54, + -54, + -60, + -60, + -66, + -72, + -72, + -78, + -84, + -90, + -96, + -102, + -114, + -120, + -120, + -120, + -120, + -126, + -126, + -138, + -150, + -168, + -186, + -210, + -234, + -258, + -288, + -312, + -324, + -330, + -318, + -300, + -276, + -246, + -234, + -210, + -186, + -156, + -150, + -144, + -138, + -138, + -138, + -138, + -132, + -132, + -126, + -120, + -114, + -108, + -102, + -96, + -90, + -84, + -78, + -78, + -66, + -66, + -60, + -60, + -54, + -42, + -42, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -48, + -54, + -66, + -102, + -126, + -150, + -180, + -204, + -222, + -234, + -246, + -258, + -264, + -270, + -276, + -288, + -300, + -318, + -336, + -348, + -354, + -360, + -330, + -324, + -318, + -294, + -264, + -222, + -180, + -150, + -132, + -126, + -120, + -126, + -126, + -120, + -114, + -102, + -84, + -72, + -54, + -42, + -36, + -30, + -18, + 0, + 30, + 72, + 180, + 234, + 282, + 312, + 324, + 306, + 264, + 198, + 120, + 30, + -54, + -126, + -180, + -216, + -228, + -228, + -210, + -180, + -150, + -120, + -96, + -72, + -60, + -48, + -42, + -36, + -42, + -42, + -60, + -66, + -66, + -66, + -60, + -48, + -36, + -6, + 12, + 36, + 60, + 84, + 108, + 120, + 132, + 132, + 120, + 126, + 114, + 120, + 120, + 126, + 132, + 114, + 120, + 120, + 126, + 126, + 132, + 132, + 132, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 102, + 102, + 102, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 60, + 60, + 54, + 54, + 48, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 18, + 18, + 18, + 18, + 12, + 6, + 0, + 0, + 6, + 12, + 18, + 24, + 30, + 36, + 42, + 54, + 72, + 96, + 126, + 174, + 222, + 276, + 324, + 366, + 390, + 390, + 372, + 330, + 264, + 186, + 96, + 12, + -60, + -114, + -150, + -162, + -162, + -138, + -108, + -72, + -42, + -18, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -36, + -36, + -36, + -42, + -36, + -36, + -36, + -42, + -42, + -48, + -24, + -36, + -42, + -54, + -66, + -78, + -90, + -102, + -108, + -126, + -150, + -174, + -186, + -198, + -204, + -204, + -198, + -168, + -144, + -126, + -114, + -102, + -96, + -90, + -84, + -78, + -72, + -72, + -66, + -60, + -54, + -54, + -48, + -48, + -42, + -42, + -36, + -54, + -30, + -30, + -30, + -30, + -36, + -54, + -72, + -60, + -36, + -36, + -42, + -66, + -78, + -78, + -72, + -60, + -36, + -12, + 0, + -6, + -36, + -96, + -192, + -306, + -438, + -558, + -666, + -744, + -774, + -762, + -708, + -624, + -516, + -408, + -300, + -210, + -138, + -84, + -36, + 0, + 36, + 84, + 144, + 210, + 270, + 324, + 366, + 372, + 354, + 306, + 234, + 144, + 54, + -36, + -108, + -156, + -186, + -192, + -180, + -168, + -150, + -132, + -126, + -132, + -138, + -150, + -168, + -174, + -180, + -156, + -144, + -144, + -144, + -144, + -138, + -108, + -102, + -102, + -108, + -102, + -96, + -78, + -72, + -66, + -60, + -54, + -48, + -42, + -36, + -30, + -24, + -24, + -18, + -12, + -6, + 0, + 6, + 12, + 18, + 24, + 30, + 36, + 42, + 42, + 48, + 54, + 60, + 66, + 72, + 78, + 78, + 84, + 90, + 96, + 102, + 102, + 108, + 114, + 120, + 120, + 126, + 132, + 132, + 138, + 138, + 144, + 144, + 150, + 156, + 156, + 156, + 162, + 162, + 162, + 162, + 162, + 162, + 168, + 168, + 168, + 168, + 168, + 168, + 162, + 162, + 162, + 162, + 162, + 162, + 156, + 156, + 150, + 150, + 144, + 138, + 156, + 150, + 144, + 156, + 150, + 144, + 138, + 132, + 132, + 126, + 114, + 102, + 84, + 60, + 36, + 12, + -12, + -36, + -60, + -84, + -102, + -108, + -114, + -108, + -102, + -72, + -54, + -42, + -24, + -18, + -6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + -18, + -18, + -24, + -24, + -30, + -36, + -36, + -42, + -54, + -60, + -66, + -48, + -54, + -60, + -66, + -72, + -84, + -96, + -120, + -138, + -156, + -168, + -180, + -192, + -204, + -210, + -222, + -234, + -258, + -282, + -318, + -354, + -390, + -426, + -450, + -468, + -474, + -468, + -456, + -438, + -408, + -390, + -384, + -372, + -360, + -348, + -354, + -342, + -336, + -330, + -318, + -312, + -306, + -300, + -294, + -288, + -276, + -270, + -264, + -252, + -258, + -252, + -240, + -234, + -228, + -216, + -210, + -198, + -192, + -180, + -168, + -162, + -150, + -138, + -126, + -114, + -108, + -96, + -72, + -60, + -54, + -48, + -42, + -36, + -30, + -18, + -12, + -6, + 0, + 6, + 12, + 18, + 18, + 24, + 30, + 30, + 42, + 42, + 36, + 24, + 12, + 0, + -18, + -18, + -12, + 18, + 18, + 6, + -24, + -72, + -144, + -222, + -306, + -384, + -438, + -462, + -462, + -426, + -348, + -294, + -252, + -204, + -210, + -162, + -156, + -156, + -162, + -162, + -162, + -168, + -168, + -168, + -162, + -156, + -150, + -138, + -126, + -120, + -114, + -108, + -102, + -96, + -72, + -72, + -66, + -72, + -66, + -60, + -48, + -42, + -36, + -36, + -36, + -36, + -36, + -30, + -24, + -18, + 0, + 24, + 48, + 84, + 126, + 228, + 276, + 312, + 342, + 348, + 336, + 294, + 228, + 144, + 42, + -66, + -174, + -264, + -342, + -396, + -420, + -420, + -402, + -378, + -342, + -312, + -282, + -270, + -270, + -276, + -294, + -300, + -306, + -336, + -372, + -378, + -378, + -378, + -384, + -384, + -384, + -384, + -396, + -402, + -414, + -420, + -420, + -420, + -426, + -414, + -402, + -384, + -360, + -330, + -300, + -270, + -240, + -204, + -180, + -156, + -138, + -126, + -120, + -120, + -126, + -138, + -150, + -156, + -156, + -162, + -156, + -156, + -162, + -162, + -168, + -180, + -186, + -198, + -216, + -228, + -240, + -246, + -246, + -246, + -240, + -216, + -192, + -174, + -156, + -138, + -120, + -102, + -96, + -78, + -66, + -48, + -36, + -24, + -12, + 0, + 6, + 12, + 12, + 18, + 24, + 30, + 30, + 36, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 24, + 24, + 24, + 24, + 24, + 36, + 42, + 36, + 18, + 0, + -24, + -36, + -36, + -24, + 6, + 48, + 108, + 174, + 246, + 318, + 384, + 432, + 462, + 474, + 468, + 450, + 426, + 378, + 348, + 312, + 276, + 258, + 228, + 216, + 204, + 204, + 204, + 216, + 228, + 246, + 264, + 288, + 306, + 324, + 336, + 318, + 324, + 330, + 330, + 330, + 330, + 330, + 324, + 306, + 306, + 300, + 300, + 294, + 294, + 288, + 282, + 282, + 276, + 270, + 264, + 258, + 258, + 246, + 240, + 234, + 234, + 228, + 222, + 216, + 216, + 210, + 204, + 198, + 192, + 192, + 186, + 180, + 174, + 168, + 168, + 162, + 156, + 156, + 150, + 150, + 150, + 150, + 150, + 150, + 138, + 138, + 144, + 144, + 150, + 156, + 162, + 168, + 180, + 186, + 198, + 204, + 216, + 222, + 234, + 240, + 246, + 228, + 234, + 240, + 240, + 246, + 246, + 246, + 246, + 246, + 246, + 246, + 246, + 234, + 228, + 228, + 228, + 222, + 222, + 222, + 222, + 222, + 216, + 210, + 210, + 204, + 204, + 198, + 192, + 192, + 186, + 186, + 180, + 180, + 174, + 174, + 168, + 168, + 168, + 162, + 162, + 162, + 162, + 162, + 162, + 162, + 168, + 168, + 168, + 168, + 174, + 174, + 180, + 180, + 180, + 180, + 186, + 186, + 186, + 186, + 186, + 186, + 180, + 180, + 180, + 174, + 174, + 168, + 162, + 162, + 156, + 150, + 144, + 138, + 132, + 126, + 126, + 120, + 114, + 114, + 108, + 102, + 102, + 96, + 96, + 90, + 90, + 90, + 84, + 90, + 90, + 90, + 90, + 102, + 102, + 102, + 102, + 96, + 90, + 90, + 96, + 102, + 108, + 120, + 132, + 144, + 156, + 174, + 204, + 234, + 282, + 330, + 384, + 438, + 474, + 498, + 498, + 468, + 414, + 342, + 246, + 150, + 54, + -30, + -90, + -132, + -150, + -144, + -126, + -96, + -66, + -36, + 6, + 18, + 24, + 24, + 30, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 18, + 18, + 12, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 18, + 18, + 24, + 30, + 30, + 36, + 42, + 42, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -54, + -54, + -60, + -60, + -60, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -84, + -84, + -84, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -96, + -96, + -108, + -108, + -108, + -108, + -102, + -102, + -96, + -96, + -90, + -90, + -84, + -84, + -78, + -72, + -72, + -66, + -66, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -54, + -60, + -60, + -66, + -72, + -84, + -90, + -96, + -102, + -114, + -120, + -126, + -132, + -144, + -150, + -150, + -156, + -162, + -162, + -162, + -162, + -156, + -156, + -150, + -150, + -138, + -132, + -126, + -126, + -120, + -114, + -108, + -102, + -96, + -90, + -84, + -84, + -78, + -72, + -54, + -48, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -30, + -24, + -18, + -12, + 0, + 6, + 18, + 24, + 42, + 48, + 78, + 114, + 162, + 210, + 258, + 300, + 318, + 318, + 294, + 240, + 168, + 78, + -18, + -114, + -192, + -252, + -288, + -300, + -288, + -258, + -228, + -192, + -162, + -150, + -138, + -138, + -132, + -132, + -144, + -144, + -168, + -168, + -168, + -174, + -198, + -198, + -198, + -198, + -198, + -198, + -198, + -198, + -198, + -216, + -216, + -210, + -210, + -204, + -198, + -192, + -180, + -174, + -150, + -138, + -132, + -120, + -108, + -102, + -96, + -84, + -78, + -72, + -66, + -60, + -54, + -54, + -60, + -54, + -48, + -42, + -42, + -36, + -30, + -24, + -24, + -18, + -12, + -12, + -6, + 0, + 6, + 6, + 12, + 18, + 18, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 30, + 30, + 30, + 30, + 24, + 18, + 18, + 12, + 6, + 0, + 0, + -6, + -12, + -18, + -24, + -30, + -36, + -42, + -48, + -54, + -60, + -66, + -78, + -84, + -84, + -90, + -96, + -102, + -108, + -114, + -114, + -120, + -126, + -132, + -132, + -138, + -144, + -150, + -174, + -180, + -180, + -180, + -192, + -180, + -180, + -186, + -192, + -192, + -192, + -192, + -186, + -192, + -192, + -204, + -222, + -258, + -306, + -372, + -444, + -516, + -588, + -642, + -666, + -660, + -636, + -594, + -528, + -468, + -414, + -360, + -342, + -300, + -270, + -246, + -234, + -234, + -240, + -258, + -288, + -312, + -366, + -384, + -396, + -402, + -408, + -414, + -420, + -408, + -378, + -348, + -312, + -276, + -234, + -204, + -174, + -150, + -132, + -120, + -108, + -102, + -96, + -84, + -78, + -72, + -66, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -60, + -66, + -72, + -72, + -78, + -90, + -96,-102, + -108, + -108, + -114, + -108, + -114, + -114, + -120, + -120, + -126, + -126, + -132, + -132, + -126, + -126, + -126, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -132, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -144, + -144, + -144, + -144, + -144, + -150, + -150, + -150, + -150, + -156, + -156, + -156, + -162, + -162, + -168, + -168, + -174, + -174, + -180, + -180, + -186, + -186, + -192, + -198, + -198, + -204, + -204, + -210, + -216, + -216, + -228, + -228, + -234, + -240, + -240, + -252, + -252, + -252, + -258, + -258, + -258, + -264, + -264, + -264, + -264, + -264, + -264, + -270, + -270, + -270, + -270, + -270, + -276, + -276, + -282, + -288, + -294, + -294, + -300, + -300, + -306, + -312, + -318, + -330, + -342, + -354, + -372, + -384, + -402, + -420, + -438, + -456, + -468, + -480, + -486, + -492, + -498, + -504, + -510, + -516, + -510, + -516, + -516, + -522, + -522, + -528, + -528, + -528, + -528, + -528, + -528, + -522, + -522, + -516, + -510, + -510, + -504, + -498, + -498, + -492, + -486, + -486, + -480, + -474, + -468, + -468, + -462, + -456, + -456, + -450, + -450, + -450, + -450, + -444, + -444, + -444, + -444, + -444, + -450, + -450, + -450, + -462, + -462, + -462, + -468, + -468, + -468, + -474, + -474, + -480, + -480, + -480, + -486, + -486, + -492, + -492, + -498, + -504, + -504, + -510, + -510, + -516, + -522, + -522, + -528, + -528, + -534, + -540, + -540, + -546, + -546, + -552, + -558, + -558, + -564, + -570, + -570, + -576, + -576, + -582, + -582, + -582, + -588, + -588, + -588, + -594, + -594, + -594, + -594, + -594, + -600, + -600, + -600, + -600, + -606, + -606, + -606, + -606, + -612, + -612, + -612, + -618, + -618, + -624, + -624, + -630, + -636, + -636, + -630, + -648, + -648, + -654, + -660, + -666, + -672, + -678, + -684, + -690, + -696, + -702, + -708, + -714, + -720, + -726, + -732, + -744, + -750, + -756, + -762, + -780, + -786, + -792, + -798, + -798, + -798, + -786, + -786, + -786, + -792, + -810, + -828, + -852, + -870, + -882, + -888, + -882, + -870, + -852, + -834, + -822, + -822, + -834, + -852, + -882, + -912, + -924, + -924, + -894, + -828, + -738, + -618, + -474, + -324, + -180, + -48, + 66, + 150, + 210, + 246, + 264, + 270, + 276, + 276, + 282, + 288, + 300, + 312, + 330, + 342, + 348, + 354, + 354, + 360, + 366, + 372, + 372, + 378, + 384, + 390, + 390, + 390, + 396, + 396, + 402, + 402, + 408, + 414, + 414, + 414, + 408, + 414, + 414, + 414, + 414, + 420, + 420, + 420, + 420, + 420, + 420, + 420, + 420, + 420, + 420, + 420, + 420, + 414, + 414, + 414, + 414, + 414, + 408, + 408, + 408, + 408, + 408, + 402, + 402, + 402, + 402, + 402, + 402, + 402,396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 396, + 390, + 390, + 390, + 390, + 390, + 384, + 384, + 384, + 384, + 378, + 378, + 378, + 372, + 372, + 366, + 366, + 360, + 360, + 354, + 354, + 348, + 348, + 342, + 336, + 336, + 330, + 324, + 324, + 318, + 312, + 312, + 306, + 300, + 294, + 288, + 282, + 282, + 276, + 270, + 264, + 258, + 252, + 246, + 240, + 234, + 228, + 222, + 216, + 210, + 210, + 204, + 186, + 180, + 174, + 168, + 162, + 156, + 150, + 144, + 138, + 138, + 126, + 120, + 114, + 108, + 102, + 102, + 108, + 114, + 120, + 126, + 132, + 138, + 144, + 150, + 162, + 174, + 198, + 228, + 276, + 324, + 372, + 414, + 450, + 462, + 456, + 426, + 372, + 300, + 216, + 126, + 60, + 0, + -48, + -78, + -90, + -90, + -78, + -54, + -30, + -12, + 6, + 18, + 18, + 12, + 12, + -6, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -42, + -42, + -48, + -48, + -54, + -54, + -60, + -66, + -78, + -84, + -90, + -96, + -102, + -102, + -108, + -114, + -120, + -126, + -132, + -132, + -138, + -144, + -144, + -150, + -156, + -156, + -162, + -162, + -168, + -168, + -168, + -168, + -174, + -174, + -174, + -174, + -186, + -186, + -180, + -180, + -174, + -174, + -168, + -168, + -162, + -156, + -156, + -150, + -144, + -144, + -138, + -132, + -132, + -126, + -126, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -144, + -144, + -150, + -150, + -156, + -156, + -162, + -168, + -168, + -174, + -180, + -174, + -180, + -186, + -192, + -198, + -204, + -216, + -222, + -228, + -234, + -240, + -252, + -258, + -264, + -270, + -276, + -294, + -300, + -306, + -330, + -330, + -324, + -318, + -312, + -300, + -294, + -288, + -276, + -264, + -252, + -246, + -228, + -216, + -210, + -198, + -192, + -186, + -180, + -174, + -174, + -174, + -174, + -174, + -180, + -180, + -174, + -174, + -174, + -168, + -168, + -168, + -162, + -162, + -156, + -150, + -144, + -144, + -132, + -126, + -120, + -108, + -102, + -78, + -66, + -54, + -60, + -48, + -36, + -24, + -12, + 0, + 18, + 30, + 42, + 54, + 66, + 78, + 90, + 102, + 114, + 126, + 138, + 150, + 156, + 168, + 174, + 180, + 186, + 192, + 198, + 204, + 210, + 192, + 192, + 198, + 198, + 204, + 204, + 204, + 204, + 210, + 210, + 210, + 210, + 210, + 210, + 210, + 204, + 204, + 204, + 204, + 204, + 204, + 198, + 198, + 198, + 198, + 192, + 192, + 192, + 192, + 186, + 186, + 186, + 186, + 180, + 180, + 180, + 180, + 180, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 174, + 180, + 180, + 180, + 180, + 180, + 180, + 180, + 180, + 186, + 186, + 186, + 186, + 186, + 186, + 186, + 186, + 186, + 186, + 180, + 180, + 180, + 180, + 174, + 174, + 174, + 168, + 168, + 168, + 162, + 162, + 156, + 156, + 156, + 150, + 150, + 144, + 144, + 144, + 138,138, + 138, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 120, + 120, + 126, + 126, + 126, + 126, + 126, + 132, + 126, + 126, + 132, + 120, + 114, + 114, + 120, + 150, + 156, + 162, + 162, + 168, + 168, + 174, + 174, + 174, + 174, + 174, + 174, + 168, + 162, + 156, + 150, + 144, + 126, + 108, + 66, + 30, + -6, + -30, + -96, + -120, + -132, + -126, + -102, + -78, + -48, + -12, + 42, + 96, + 138, + 150, + 132, + 72, + -12, + -126, + -252, + -378, + -486, + -570, + -624, + -654, + -672, + -690, + -720, + -786, + -888, + -1032, + -1212, + -1404, + -1596, + -1758, + -1884, + -1962, + -1980, + -1944, + -1878, + -1782, + -1686, + -1602, + -1530, + -1482, + -1452, + -1434, + -1422, + -1398, + -1368, + -1260, + -1194, + -1128, + -1074, + -1032, + -1008, + -1002, + -996, + -996, + -990, + -978, + -948, + -894, + -858, + -810, + -786, + -774, + -780, + -756, + -738, + -714, + -690, + -672, + -654, + -636, + -618, + -600, + -582, + -564, + -546, + -534, + -516, + -504, + -486, + -474, + -462, + -450, + -438, + -426, + -414, + -402, + -390, + -384, + -372, + -360, + -354, + -342, + -336, + -330, + -318, + -312, + -306, + -300, + -288, + -282, + -276, + -264, + -264, + -258, + -246, + -246, + -240, + -240, + -234, + -228, + -222, + -222, + -216, + -204, + -210, + -192, + -192, + -180, + -174, + -186, + -174, + -168,-162, + -150, + -132, + -108, + -72, + -30, + 24, + 78, + 132, + 186, + 234, + 282, + 324, + 360, + 390, + 408, + 426, + 444, + 450, + 462, + 468, + 474, + 480, + 504, + 498, + 510, + 480, + 492, + 498, + 510, + 516, + 528, + 534, + 540, + 546, + 552, + 552, + 558, + 564, + 564, + 570, + 570, + 570, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 576, + 570, + 570, + 570, + 570, + 570, + 570, + 564, + 564, + 564, + 564, + 564, + 558, + 558, + 558, + 558, + 552, + 552, + 552, + 546, + 546, + 540, + 540, + 534, + 534, + 528, + 522, + 522, + 516, + 510, + 510, + 504, + 498, + 492, + 486, + 486, + 480, + 474, + 468, + 468, + 462, + 456, + 450, + 444, + 438, + 432, + 432, + 426, + 420, + 414, + 414, + 408, + 402, + 402, + 396, + 390, + 384, + 384, + 378, + 372, + 372, + 366, + 360, + 360, + 354, + 354, + 348, + 342, + 342, + 336, + 336, + 336, + 330, + 330, + 330, + 324, + 324, + 324, + 318, + 318, + 318, + 318, + 318, + 312, + 312, + 312, + 312, + 306, + 306, + 306, + 306, + 300, + 300, + 300, + 294, + 294, + 288, + 288, + 282, + 282, + 276, + 276, + 270, + 270, + 264, + 258, + 258, + 252, + 252, + 246, + 246, + 240, + 240, + 234, + 234, + 228, + 228, + 222, + 222, + 216, + 216, + 210, + 210, + 204, + 204, + 204, + 204, + 180, + 180, + 168, + 180, + 174, + 168, + 162, + 150, + 138, + 132, + 126, + 126, + 120, + 132, + 162, + 174, + 204, + 252, + 288, + 342, + 390, + 444, + 474, + 504, + 516, + 504, + 480, + 414, + 354, + 282, + 204, + 150, + 90, + 48, + 24, + 12, + 24, + 42, + 60, + 84, + 102, + 96, + 114, + 114, + 114, + 96, + 84, + 84, + 102, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 60, + 54, + 48, + 48, + 42, + 36, + 36, + 30, + 18, + 12, + 6, + -6, + -12, + -24, + -30, + -36, + -48, + -54, + -60, + -60, + -66, + -72, + -72, + -66, + -66, + -66, + -60, + -54, + -48, + -42, + -36, + -30, + -18, + -12, + -6, + 0, + 12, + 18, + 24, + 30, + 36, + 42, + 42, + 48, + 48, + 48, + 48, + 42, + 42, + 36, + 36, + 12, + 6, + 6, + 0, + 0, + -6, + -12, + -12, + -18, + -18, + -24, + -42, + -48, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -24, + -24, + -12, + -12, + -6, + -6, + -6, + -12, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 6, + 6, + 12, + 18, + 24, + 30, + 36, + 36, + 42, + 48, + 54, + 60, + 66, + 72, + 72, + 78, + 84, + 84, + 90, + 96, + 96, + 96, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 60, + 42, + 42, + 42, + 30, + 30, + 24, + 18, + 6, + 0, + -6, + -6, + -6, + 12, + 24, + 54, + 78, + 114, + 144, + 180, + 228, + 282, + 324, + 348, + 360, + 354, + 324, + 282, + 222, + 162, + 96, + 36, + -18, + -60, + -90, + -108, + -114, + -108, + -90, + -72, + -42, + -18, + 6, + 18, + 42, + 54, + 60, + 42, + 30, + 36, + 42, + 48, + 48, + 54, + 60, + 66, + 72, + 72, + 66, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 66, + 66, + 66, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 30, + 36, + 36, + 42, + 42, + 48, + 54, + 60, + 54, + 42, + 24, + 12, + -6, + -18, + -30, + -30, + -24, + -12, + 0, + 18, + 36, + 42, + 36, + 12, + -36, + -114, + -222, + -360, + -528, + -726, + -1164, + -1386, + -1596, + -1788, + -1938, + -2052, + -2124, + -2148, + -2136, + -2082, + -2004, + -1908, + -1800, + -1692, + -1584, + -1488, + -1404, + -1326, + -1260, + -1206, + -1158, + -1110, + -1074, + -1032, + -996, + -948, + -900, + -846, + -780, + -708, + -636, + -552, + -474, + -390, + -306, + -228, + -156, + -78, + -6, + 66, + 144, + 222, + 288, + 354, + 402, + 438, + 456, + 462, + 462, + 420, + 360, + 294, + 222, + 174, + 132, + 102, + 84, + 90, + 120, + 144, + 174, + 192, + 216, + 228, + 240, + 252, + 264, + 252, + 246, + 252, + 258, + 258, + 264, + 264, + 270, + 270, + 270, + 276, + 276, + 282, + 282, + 282, + 282, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 288, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 282, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 276, + 270, + 270, + 270, + 270, + 270, + 270, + 264, + 264, + 264, + 264, + 264, + 258, + 258, + 258, + 252, + 252, + 252, + 246, + 246, + 240, + 240, + 240, + 234, + 234, + 228, + 228, + 222, + 222, + 216, + 216, + 210, + 210, + 204, + 204, + 198, + 192, + 192, + 186, + 186, + 180, + 174, + 174, + 168, + 168, + 162, + 162, + 156, + 150, + 144, + 144, + 138, + 138, + 132, + 126, + 126, + 120, + 120, + 114, + 114, + 108, + 108, + 102, + 102, + 96, + 96, + 90, + 90, + 84, + 84, + 84, + 78, + 78, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -24, + -30, + -30, + -30, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -78, + -78, + -78, + -102, + -102, + -108, + -102, + -96, + -90, + -60, + -36, + -12, + 18, + 48, + 78, + 114, + 150, + 192, + 222, + 264, + 294, + 306, + 306, + 288, + 216, + 144, + 96, + 42, + 0, + -42, + -78, + -108, + -132, + -144, + -144, + -150, + -132, + -114, + -102, + -84, + -90, + -66, + -60, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -72, + -72, + -78, + -54, + -54, + -66, + -72, + -78, + -90, + -102, + -114, + -126, + -138, + -156, + -168, + -180, + -192, + -204, + -252, + -264, + -300, + -306, + -306, + -306, + -294, + -282, + -258, + -246, + -216, + -204, + -198, + -186, + -174, + -162, + -150, + -138, + -120, + -108, + -102, + -96, + -108, + -102, + -102, + -96, + -90, + -90, + -84, + -84, + -78, + -78, + -78, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -48, + -48, + -48,-48, + -48, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 18, + 24, + 24, + 30, + 36, + 42, + 48, + 54, + 60, + 48, + 60, + 72, + 84, + 96, + 108, + 120, + 132, + 144, + 156, + 168, + 180, + 228, + 258, + 288, + 318, + 342, + 354, + 354, + 342, + 318, + 282, + 240, + 192, + 150, + 144, + 120, + 96, + 78, + 54, + 36, + -18, + -12, + -6, + 6, + 18, + 12, + 18, + 48, + 78, + 78, + 84, + 84, + 84, + 90, + 90, + 90, + 96, + 102, + 102, + 108, + 108, + 114, + 114, + 120, + 120, + 126, + 126, + 126, + 126, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 120, + 126, + 126, + 126, + 126, + 126, + 126, + 126, + 120, + 120, + 120, + 120, + 120, + 114, + 114, + 108, + 108, + 108, + 102, + 102, + 96, + 96, + 90, + 90, + 84, + 84, + 78, + 78, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -6, + 0, + 0, + -12, + -6, + 0, + 12, + 18, + 30, + 42, + 54,60, + 72, + 78, + 120, + 162, + 186, + 210, + 234, + 240, + 240, + 228, + 192, + 156, + 108, + 66, + 18, + 12, + -12, + -30, + -54, + -138, + -144, + -144, + -144, + -132, + -120, + -120, + -114, + -84, + -84, + -84, + -66, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -84, + -84, + -90, + -96, + -102, + -66, + -72, + -48, + -42, + -36, + -12, + -12, + -12, + -30, + -54, + -90, + -132, + -186, + -252, + -312, + -378, + -438, + -498, + -540, + -582, + -612, + -630, + -642, + -654, + -654, + -660, + -654, + -654, + -654, + -642, + -642, + -636, + -630, + -624, + -618, + -606, + -594, + -582, + -564, + -546, + -522, + -498, + -468, + -438, + -402, + -360, + -324, + -282, + -240, + -198, + -156, + -126, + -90, + -66, + -42, + -18, + -18, + 6, + -24, + -6, + 12, + -30, + -12, + 0, + 18, + 24, + 36, + 42, + 48, + 54, + 60, + 60, + 66, + 66, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 90, + 84, + 126, + 120, + 144, + 114, + 120, + 132, + 126, + 132, + 150, + 162, + 168, + 162, + 144, + 108, + 60, + -12, + -78, + -174, + -276, + -366, + -456, + -528, + -504, + -450, + -378, + -294, + -222, + -162, + -102, + -78, + -42, + -30, + 0, + 18, + 30, + 42, + 36, + 48, + 60, + 72, + 84, + 96, + 102, + 102, + 102, + 108, + 84, + 90, + 96, + 102, + 108, + 114, + 114, + 120, + 126, + 126, + 126, + 132, + 132, + 132, + 138, + 138, + 138, + 138, + 138, + 138, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 144, + 138, + 138, + 138, + 138, + 132, + 132, + 132, + 132, + 126, + 126, + 126, + 120, + 120, + 120, + 114, + 114, + 108, + 108, + 108, + 102, + 102, + 102, + 96, + 96, + 96, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 90, + 90, + 96, + 102, + 108, + 108, + 114, + 120, + 126, + 126, + 132, + 132, + 138, + 138, + 168, + 174, + 180, + 180, + 234, + 234, + 288, + 294, + 288, + 204, + 186, + 168, + 138, + 126, + 114, + 102, + 90, + 72, + 66, + 54, + 42, + 30, + 24, + 18, + 12, + 12, + 6, + 6, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -6, + 0, + -18, + -12, + 0, + 6, + 18, + 24, + 36, + 42, + 54, + 60, + 72, + 108, + 126, + 144, + 168, + 192, + 210, + 216, + 216, + 204, + 180, + 144, + 108, + 54, + 12, + -36, + -78, + -114, + -144, + -168, + -186, + -192, + -198, + -186, + -174, + -156, + -150, + -138, + -114, + -108, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -66, + -66, + -60, + -60, + -54, + -54, + -48, + -48, + -48, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -48, + -48, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -66, + -66, + -66, + -60, + -60, + -60, + -54, + -54, + -48, + -48, + -42, + -36, + -30, + -24, + -18, + -30, + -18, + -6, + 0, + 12, + 24, + 36, + 48, + 60, + 72, + 78, + 90, + 144, + 174, + 204, + 228, + 246, + 252, + 240, + 210, + 180, + 132, + 90, + 48, + 30, + 12, + -12, + -36, + -78, + -96, + -108, + -114, + -120, + -120, + -120, + -84, + -84, + -84, + -54, + -54, + -54, + -60, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -42, + -42, + -42, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -36, + -36, + -42, + -48, + -54, + -60, + -66, + -78, + -84, + -96, + -108, + -132, + -150, + -162, + -180, + -162, + -186, + -204, + -228, + -252, + -276, + -300, + -324, + -348, + -372, + -396, + -420, + -438, + -462, + -438, + -456, + -468, + -486, + -498, + -510, + -516, + -528, + -540, + -546, + -552, + -558, + -564, + -570, + -576, + -576, + -582, + -582, + -582, + -582, + -588, + -588, + -588, + -588, + -582, + -582, + -582, + -576, + -570, + -570, + -564, + -558, + -546, + -540, + -528, + -522, + -510, + -498, + -540, + -522, + -504, + -492, + -462, + -432, + -402, + -366, + -336, + -300, + -264, + -228, + -198, + -198, + -168, + -138, + -114, + -84, + -60, + -36, + -18, + 6, + 24, + -6, + 12, + 30, + 42, + 54, + 66, + 78, + 90, + 96, + 108, + 114, + 120, + 126, + 132, + 138, + 144, + 144, + 150, + 156, + 156, + 156, + 162, + 162, + 162, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 168, + 162, + 162, + 162, + 162, + 156, + 156, + 156, + 150, + 150, + 144, + 144, + 144, + 138, + 138, + 132, + 132, + 126, + 126, + 120, + 120, + 120, + 114, + 114, + 114, + 108, + 108, + 108, + 102, + 102, + 102, + 96, + 96, + 96, + 96, + 96, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 30, + 30, + 36, + -6, + 0, + 12, + 24, + 36, + 54, + 78, + 102, + 126, + 150, + 174, + 186, + 246, + 276, + 312, + 330, + 330, + 324, + 300, + 240, + 192, + 138, + 96, + 48, + 60, + 36, + 12, + -72, + -84, + -90, + -90, + -84, + -72, + -60, + -72, + -36, + -42, + -42, + -18, + -18, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -18, + -18, + -24, + -24, + -30, + -30, + -36, + -36, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -36, + -36, + -30, + -30, + -30, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 12, + 6, + 6, + 0, + -6, + -6, + -12, + -18, + -18, + -24, + -30, + -30, + -36, + -36, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -36, + -36, + -36, + -30, + -30, + -30, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 24, + 30, + 36, + 42, + 48, + 54, + 60, + 66, + 78, + 84, + 90, + 108, + 120, + 132, + 138, + 150, + 162, + 168, + 216, + 246, + 276, + 306, + 324, + 330, + 324, + 306, + 276, + 228, + 180, + 126, + 84, + 36, + -6, + -36, + -66, + -84, + -102, + -102, + -90, + -78, + -60, + -48, + -36, + -42, + -12, + -12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 54, + 48, + 48, + 42, + 36, + 30, + 24, + 24, + 18, + 12, + 6, + 6, + 0, + -6, + -6, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30,30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 12, + 12, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + 0, + 0, + 6, + 12, + 18, + 0, + 12, + 24, + 30, + 42, + 54, + 66, + 78, + 90, + 102, + 108, + 120, + 168, + 180, + 210, + 240, + 258, + 276, + 276, + 270, + 252, + 216, + 174, + 126, + 78, + 60, + 36, + 12, + -12, + -30, + -54, + -72, + -84, + -96, + -108, + -114, + -84, + -84, + -90, + -54, + -54, + -60, + -60, + -60, + -60, + -60, + -54, + -54, + -54, + -48, + -48, + -42, + -36, + -36, + -30, + -24, + -24, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -18, + -18, + -12, + -12, + -6, + 0, + 6, + 12, + 12, + 18, + 24, + 24, + 36, + 48, + 54, + 66, + 78, + 84, + 90, + 102, + 150, + 180, + 204, + 228, + 240, + 240, + 234, + 168, + 132, + 96, + 48, + 30, + 6, + -18, + -42, + -60, + -78, + -96, + -108, + -138, + -120, + -120, + -114, + -78, + -78, + -78, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 24, + 30, + 30, + 36, + 48, + 60, + 72, + 78, + 90, + 96, + 102, + 150, + 162, + 192, + 216, + 234, + 240, + 234, + 216, + 192, + 144, + 108, + 54, + 30, + 6, + -18, + -42, + -60, + -84, + -96, + -108, + -156, + -144, + -126, + -120, + -78, + -84, + -84, + -48, + -48, + -48, + -48, + -48, + -48, + -48, + -42, + -42, + -36, + -36, + -36, + -30, + -30, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -30, + -30, + -24, + -18, + -18, + -12, + -24, + -18, + -12, + 0, + 6, + 18, + 30, + 42, + 54, + 66, + 72, + 84, + 90, + 144, + 168, + 198, + 228, + 252, + 264, + 258, + 240, + 216, + 168, + 126, + 84, + 54, + 30, + 6, + -12, + -36, + -54, + -72, + -84, + -96, + -102, + -102, + -102, + -60, + -60, + -66, + -36, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 6, + 12, + 18, + 24, + 30, + 36, + 42, + 48, + 60, + 66, + 72, + 78, + 84, + 114, + 120, + 132, + 138, + 144, + 204, + 204, + 270, + 276, + 270, + 186, + 174, + 162, + 126, + 120, + 108, + 102, + 90, + 84, + 60, + 54, + 48, + 42, + 36, + 30, + 30, + 30, + 30, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -12, + -6, + -6, + 0, + 6, + 12, + 0, + 6, + 18, + 30, + 36, + 60, + 72, + 78, + 90, + 96, + 144, + 156, + 204, + 222, + 246, + 252, + 246, + 216, + 192, + 156, + 96, + 78, + 60, + 48, + 36, + 24, + 12, + 0, + -12, + -24, + -30, + -42, + -48, + -48, + -54, + -54, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 42, + 42, + 48, + 48, + 48, + 54, + 54, + 54, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -30, + -30, + -36, + -36, + -36, + -36, + -42, + -42, + -42,-42, + -42, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -30, + -30, + -24, + -18, + -12, + -6, + -24, + -12, + 0, + 6, + 18, + 30, + 42, + 54, + 66, + 78, + 90, + 96, + 144, + 162, + 186, + 216, + 246, + 264, + 270, + 264, + 246, + 210, + 174, + 120, + 78, + 48, + 24, + 0, + -24, + -42, + -66, + -84, + -96, + -132, + -108, + -114, + -72, + -72, + -78, + -48, + -48, + -54, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -48, + -48, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 6, + 0, + 0, + -6, + -12, + -24, + -30, + -36, + -12, + -24, + 0, + 0, + -6, + -12, + -24, + -36, + -60, + -78, + -108, + -192, + -240, + -288, + -330, + -378, + -408, + -468, + -492, + -498, + -492, + -474, + -444, + -402, + -366, + -324, + -258, + -228, + -198, + -180, + -156, + -138, + -126, + -114, + -102, + -90, + -84, + -78, + -60, + -54, + -48, + -60, + -54, + -78, + -72, + -66, + -60, + -54, + -54, + -48, + -48, + -42, + -42, + -42, + -42, + -42, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -12, + -12, + -18, + -18, + -24, + -24, + -30, + -30, + -36, + -36, + -42, + -42, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -54, + -54, + -48, + -48, + -42, + -42, + -36, + -30, + -24, + -48, + -36, + -30, + -18, + -6, + 6, + 24, + 48, + 66, + 78, + 102, + 126, + 126, + 156, + 180, + 210, + 246, + 276, + 294, + 300, + 288, + 246, + 210, + 162, + 114, + 66, + 18, + -24, + -60, + -90, + -114, + -126, + -138, + -126, + -114, + -96, + -84, + -78, + -72, + -42, + -42, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -18, + -18, + -24, + -24, + -30, + -36, + -42, + -48, + -48, + -54, + -60, + -66, + -66, + -72, + -78, + -84, + -90, + -90, + -96, + -102, + -108, + -108, + -114, + -120, + -126, + -126, + -132, + -132, + -138, + -138, + -144, + -144, + -144, + -150, + -150, + -150, + -150, + -150, + -144, + -144, + -144, + -138, + -138, + -132, + -132, + -126, + -120, + -114, + -114, + -108, + -102, + -96, + -90, + -84, + -78, + -72, + -66, + -60, + -54, + -48, + -42, + -36, + -30, + -30, + -18, + -18, + -12, + -6, + -6, + 0, + 6, + 12, + 18, + 24, + 30, + 36, + 42, + 48, + 54, + 60, + 66, + 72, + 72, + 78, + 84, + 90, + 120, + 132, + 138, + 144, + 198, + 222, + 246, + 264, + 276, + 276, + 264, + 240, + 192, + 156, + 114, + 66, + 54, + 30, + -54, + -84, + -102, + -114, + -120, + -114, + -102, + -90, + -72, + -66, + -30, + -30, + -30, + 0, + 0, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 18, + 24, + 24, + 30, + 30, + 36, + 36, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 66, + 66, + 72, + 72, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 84, + 84, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 72, + 66, + 66, + 66, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 24, + 18, + 12, + 12, + 0, + 0, + -6, + -12, + -18, + -18, + -24, + -30, + -36, + -42, + -48, + -48, + -54, + -60, + -66, + -72, + -72, + -78, + -78, + -84, + -84, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -90, + -90, + -90, + -84, + -84, + -78, + -78, + -72, + -66, + -66, + -60, + -54, + -48, + -48, + -42, + -36, + -30, + -30, + -24, + -18, + -18, + -12, + -12, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18,18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 30, + 36, + 36, + 42, + 48, + 36, + 42, + 54, + 66, + 72, + 84, + 96, + 108, + 114, + 126, + 138, + 144, + 192, + 210, + 240, + 264, + 282, + 294, + 294, + 282, + 252, + 222, + 180, + 84, + 42, + 42, + 12, + -66, + -90, + -108, + -114, + -114, + -108, + -96, + -78, + -78, + -42, + -42, + -42, + -12, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 12, + 12, + 18, + 24, + 30, + 36, + 42, + 48, + 54, + 60, + 66, + 72, + 78, + 78, + 108, + 120, + 126, + 132, + 132, + 186, + 192, + 252, + 258, + 246, + 162, + 150, + 132, + 102, + 90, + 78, + 66, + 54, + 42, + 30, + 18, + 12, + 0, + -6, + -12, + -18, + -24, + -24, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 36, + 42, + 48, + 54, + 36, + 42, + 54, + 66, + 72, + 84, + 96, + 108, + 120, + 132, + 144, + 156, + 204, + 216, + 252, + 276, + 300, + 312, + 318, + 306, + 288, + 246, + 210, + 162, + 138, + 120, + 108, + 96, + 84, + 72, + 60, + 48, + 36, + 18, + 6, + 0, + -6, + 24, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 6, + 6, + 0, + 0, + -6, + -6, + -12, + -12, + -18, + -18, + -18, + -24, + -24, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -42, + -42, + -42, + -42, + -48, + -48, + -48, + -48, + -48, + -54, + -54, + -54, + -54, + -60, + -60, + -60, + -66, + -66, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -102, + -96, + -96, + -96, + -96, + -96, + -90, + -90, + -90, + -90, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -78, + -78, + -78, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -84, + -90, + -90, + -90, + -90, + -90, + -96, + -96, + -96, + -96, + -96, + -102, + -102, + -102, + -102, + -102, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -108, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -114, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -114, + -114, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -120, + -126, + -126, + -126, + -126, + -132, + -132, + -132, + -132, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -138, + -132, + -132, + -132, + -126, + -126, + -120, + -114, + -114, + -108, + -102, + -96, + -90, + -84, + -72, + -66, + -60, + -48, + -42, + -30, + -18, + -12, + 0, + 12, + 24, + 36, + 48, + 60, + 78, + 90, + 120, + 132, + 144, + 156, + 210, + 228, + 258, + 288, + 312, + 330, + 336, + 324, + 306, + 276, + 228, + 186, + 150, + 126, + 108, + 84, + 60, + 42, + 24, + 12, + 0, + -6, + -6, + -6, + 36, + 30, + 30, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 72, + 72, + 72, + 72, + 72, + 78, + 78, + 78, + 78, + 78, + 84, + 84, + 84, + 84, + 90, + 90, + 90, + 90, + 96, + 96, + 96, + 96, + 102, + 102, + 102, + 102, + 102, + 108, + 108, + 108, + 108, + 108, + 108, + 108, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 114, + 108, + 108, + 108, + 108, + 108, + 108, + 102, + 102, + 102, + 102, + 102, + 96, + 96, + 96, + 90, + 90, + 90, + 84, + 84, + 84, + 78, + 78, + 78, + 78, + 72, + 72, + 72, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 54, + 54, + 54, + 48, + 48, + 42, + 42, + 42, + 36, + 36, + 30, + 30, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + 0, + 0, + 6, + 12, + 12, + 18, + 24, + 30, + 36, + 36, + 42, + 54, + 60, + 72, + 78, + 90, + 96, + 102, + 156, + 174, + 192, + 216, + 234, + 246, + 246, + 228, + 204, + 156, + 114, + 72, + 36, + 12, + -12, + -30, + -120, + -138, + -150, + -150, + -150, + -138, + -120, + -108, + -102, + -72, + -72, + -66, + -36, + -36, + -36, + -36, + -36, + -30, + -30, + -30, + -24, + -24, + -18, + -12, + -12, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 18, + 18, + 24, + 30, + 30, + 36, + 42, + 48, + 54, + 54, + 60, + 66, + 72, + 90, + 96, + 108, + 114, + 120, + 132, + 180, + 192, + 228, + 252, + 264, + 270, + 264, + 246, + 204, + 168, + 132, + 84, + 60, + 36, + 18, + -6, + -30, + -48, + -60, + -72, + -84, + -90, + -84, + -48, + -48, + -48, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 12, + 12, + 18, + 18, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 0, + 0, + -6, + -6, + -12, + -12, + -18, + -24, + -24, + -30, + -36, + -36, + -42, + -42, + -48, + -54, + -54, + -60, + -60, + -66, + -66, + -72, + -72, + -72, + -78, + -78, + -78, + -78, + -78, + -72, + -72, + -72, + -72, + -66, + -66, + -66, + -60, + -60, + -60, + -54, + -54, + -48, + -48, + -42, + -42, + -42, + -36, + -36, + -30, + -30, + -30, + -24, + -24, + -24, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 24, + 24, + 30, + 36, + 36, + 42, + 48, + 36, + 48, + 54, + 66, + 78, + 84, + 96, + 108, + 114, + 120, + 132, + 180, + 198, + 222, + 276, + 288, + 282, + 264, + 240, + 204, + 150, + 102, + 72, + 48, + 24, + 0, + -24, + -42, + -60, + -120, + -120, + -108, + -96, + -90, + -84, + -48, + -48, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12,-12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -18, + 0, + 0, + -6, + -12, + -18, + -24, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -30, + -54, + -78, + -114, + -156, + -198, + -240, + -282, + -300, + -324, + -378, + -396, + -336, + -288, + -240, + -198, + -162, + -132, + -120, + -114, + -108, + -102, + -96, + -84, + -66, + -36, + -12, + 30, + 78, + 126, + 168, + 216, + 258, + 294, + 324, + 324, + 318, + 288, + 234, + 174, + 114, + 30, + -12, + -54, + -84, + -108, + -114, + -114, + -108, + -96, + -84, + -72, + -54, + -66, + -54, + -36, + -36, + -36, + -30, + -30, + -24, + -24, + -18, + -12, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -24, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -24, + -24, + -12, + -18, + -18, + -18, + -18, + -18, + -12, + 0, + 6, + 36, + 48, + 78, + 108, + 144, + 180, + 216, + 258, + 294, + 318, + 336, + 330, + 306, + 270, + 198, + 150, + 6, + -42, + -84, + -114, + -132, + -144, + -144, + -162, + -156, + -144, + -96, + -84, + -72, + -60, + -54, + -42, + -60, + -48, + -60, + -60, + -54, + -48, + -42, + -36, + -30, + -30, + -24, + -18, + -18, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6,6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 18, + 18, + 18, + 18, + 18, + 24, + 24, + 24, + 24, + 24, + 24, + 30, + 30, + 30, + 30, + 30, + 30, + 36, + 36, + 36, + 36, + 36, + 36, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 42, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 24, + 24, + 24, + 18, + 18, + 18, + 12, + 6, + 6, + 6, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -18, + -18, + -18, + -18, + -18, + -24, + -24, + -24, + -30, + -30, + -30, + -30, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -36, + -42, + -42, + -36, + -36, + -30, + -30, + -24, + -12, + -6, + 6, + 6, + 18, + 42, + 60, + 96, + 132, + 174, + 222, + 270, + 312, + 330, + 318, + 282, + 222, + 132, + 24, + -96, + -210, + -306, + -384, + -432, + -450, + -450, + -414, + -366, + -306, + -252, + -204, + -162, + -132, + -114, + -90, + -72, + -54, + -54, + -48, + -42, + -36, + -30, + -30, + -30, + -30, + -30, + -24, + -24, + -18, + -18, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 18, + 18, + 18, + 24, + 24, + 24, + 30, + 30, + 30, + 36, + 36, + 36, + 42, + 42, + 42, + 48, + 48, + 48, + 54, + 54, + 54, + 54, + 60, + 60, + 60, + 60, + 60, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 66, + 60, + 60, + 60, + 60, + 60, + 60, + 60, + 54, + 54, + 54, + 54, + 54, + 48, + 48, + 48, + 48, + 48, + 48, + 42, + 42, + 42, + 42, + 42, + 42, + 36, + 36, + 36, + 36, + 36, + 30, + 30, + 30, + 30, + 24, + 24, + 24, + 24, + 18, + 18, + 18, + 18, + 12, + 12, + 12, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6, + -6, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -12, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -6, + -6, + -6, + -6, + -6,-6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + -6, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 6, + 6, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 6, + 6, + 6, + 0, + 0, + -6, + -6, + -12, + -12, + -18 + ] \ No newline at end of file diff --git a/ecg/index.js b/ecg/index.js new file mode 100644 index 0000000..634b47f --- /dev/null +++ b/ecg/index.js @@ -0,0 +1,651 @@ +import { + hrDataArray +} from './hrdata.js' +const SendCommandUtil = require('../utils/SendCommandUtil.js'); // 引用 发送指令工具类 +Page({ + data: { + // 渲染物品的动态CSS, + xuanranCss:{ + width:'0', + height:'0' + }, + arrList: [], + totalReceivedNodes: 0, // 记录设备推送的总节点数 + totalConsumedNodes: 0, // 记录消费的总节点数 + setInter: null, + getDataIndex: 0, + heartRateData: [], // 当前绘制的数据 + windowSize: 1800, // 可视窗口大小 + canvasWidth: 800, + canvasHeight: 200, + maxLevel: 2000, // 最大电平值 + minLevel: -2000, // 最小电平值 + isNotifyBLEC: false, //是否订阅 + dataQueue: [], // 缓存接收到的长数组数据 + isDrawing: false, // 绘制状态标志,避免重复触发 + // deviceId: "66:3A:12:88:01:20", + deviceId: "66:3A:12:88:01:20", + serviceId: "000001FF-3C17-D293-8E48-14FE2E4DA212", + ecgCharacteristicId: "0000FF0A-0000-1000-8000-00805F9B34FB", + canvasList: [], // Canvas 列表,用于动态生成 Canvas + pointsPerCanvas: 1800, // 每个 Canvas 绘制的点数 + }, + onReady() { + // 初始化画布 + this.ctx = wx.createCanvasContext('ecgCanvas'); + // this.startDrawing(); + this.notifyBLEC(); + }, + + // 滑动窗口更新数据 + pushHeartRate(newData) { + let heartRateData = this.data.heartRateData; + + // 添加新数据 + heartRateData.push(...newData); + + // 如果数据超过窗口大小,移除最早的数据 + if (heartRateData.length > this.data.windowSize) { + heartRateData.splice(0, heartRateData.length - this.data.windowSize); + } + + // 动态计算最大值和最小值 + // const maxLevel = Math.max(...heartRateData); + // const minLevel = Math.min(...heartRateData); + + this.setData({ + heartRateData, + // maxLevel, + // minLevel + }); + }, + + // 绘制 ECG 曲线 + drawECG() { + const ctx = this.ctx; + const { + heartRateData, + canvasHeight, + maxLevel, + minLevel, + canvasWidth, + windowSize + } = this.data; + + // 清空画布 + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + + // 绘制网格 + this.drawGrid(ctx); + + // 计算缩放比例 + const scaleY = canvasHeight / (maxLevel - minLevel); // 数据缩放到画布高度范围 + const step = canvasWidth / (windowSize - 1); // 每点的水平间隔 + + // 绘制心电图 + ctx.beginPath(); + heartRateData.forEach((value, index) => { + const x = index * step; + const y = canvasHeight - (value - minLevel) * scaleY; + if (index === 0) { + ctx.moveTo(x, y); + } else { + const prevX = (index - 1) * step; + const prevY = canvasHeight - (heartRateData[index - 1] - minLevel) * scaleY; + // 使用二次贝塞尔曲线平滑点之间的连接 + const midX = (prevX + x) / 2; + const midY = (prevY + y) / 2; + ctx.quadraticCurveTo(prevX, prevY, midX, midY); + } + }); + ctx.setStrokeStyle('red'); + ctx.setLineWidth(0.5); + ctx.stroke(); + ctx.draw(); + + }, + + // 绘制网格 + drawGrid(ctx) { + const { + canvasWidth, + canvasHeight, + } = this.data; + var width = canvasWidth + var height = canvasHeight + var gridWidth = 5 + const hLineNum = parseInt(height / gridWidth); + const vLineNum = parseInt(width / gridWidth); + console.log(hLineNum, vLineNum) + ctx.setLineWidth(1); + ctx.setStrokeStyle("#ccc"); + + // 横线 + for (let i = 0; i < hLineNum; i++) { + if (i === 0 || i % 5 !== 0) { + ctx.beginPath(); + ctx.moveTo(0, i * gridWidth); + ctx.lineTo(width, i * gridWidth); + ctx.stroke(); + } + } + + // 竖线 + for (let i = 0; i < vLineNum; i++) { + if (i === 0 || i % 5 !== 0) { + ctx.beginPath(); + ctx.moveTo(i * gridWidth, 0); + ctx.lineTo(i * gridWidth, height); + ctx.stroke(); + } + } + + // 粗线 + ctx.setStrokeStyle("#9B9B9B"); + for (let i = 5; i <= vLineNum; i += 5) { + ctx.beginPath(); + ctx.moveTo(i * gridWidth, 0); + ctx.lineTo(i * gridWidth, height); + ctx.stroke(); + } + for (let i = 5; i <= hLineNum; i += 5) { + ctx.beginPath(); + ctx.moveTo(0, i * gridWidth); + ctx.lineTo(width, i * gridWidth); + ctx.stroke(); + } + }, + + + + // 启动绘制逻辑 + startDrawing() { + if (this.data.isDrawing) return; // 避免重复启动 + this.setData({ + isDrawing: true + }); + + const pointsPerRender = 50; // 每次绘制的点数,控制绘制速度 + const interval = 100; // 绘制间隔(ms) + + const drawInterval = setInterval(() => { + const { + dataQueue, + heartRateData, + windowSize + } = this.data; + + // 如果队列为空,停止绘制 + if (dataQueue.length === 0) { + clearInterval(drawInterval); + this.setData({ + isDrawing: false + }); + return; + } + + // 从队列中取出一批数据 + const newData = dataQueue.splice(0, pointsPerRender); + // 更新消费的总节点数 + this.setData({ + totalConsumedNodes: this.data.totalConsumedNodes + newData.length + }); + // 更新心率数据(保持滑动窗口) + const updatedData = heartRateData.concat(newData); + if (updatedData.length > windowSize) { + updatedData.splice(0, updatedData.length - windowSize); + } + + this.setData({ + heartRateData: updatedData, + dataQueue + }); + this.drawECG(); // 绘制心电图 + }, interval); + }, + stopECG() { + clearInterval(this.data.setInter) + }, + startECG() { + // this.startDrawing() + SendCommandUtil.getECGData(this.data.deviceId) + }, + notifyBLEC() { + wx.notifyBLECharacteristicValueChange({ + deviceId: this.data.deviceId, // 蓝牙设备 ID + serviceId: this.data.serviceId, // 蓝牙服务 UUID + characteristicId: this.data.ecgCharacteristicId, // 心电 + state: true, + success: (res) => { + console.log('心电特征订阅成功', res); + this.setData({ + isNotifyBLEC: true + }) + this.subscribeAndReceiveData(); + }, + fail: (err) => { + console.error('特征订阅失败', err); + }, + }); + }, + // QRS滤波器处理函数 + applyQRSFilter(rawData) { + const filteredData = []; + + // 高通滤波器移除基线漂移 + const highPassData = rawData.map((value, index, arr) => { + if (index === 0) return value; + return value - arr[index - 1] + 0.95 * (arr[index - 1] || 0); + }); + + // 微分滤波器增强信号 + const diffData = highPassData.map((value, index, arr) => { + if (index < 4) return 0; // 前几个点无法计算微分 + return ( + (2 * arr[index] + + arr[index - 1] - + arr[index - 3] - + 2 * arr[index - 4]) / + 8 + ); + }); + + // 平滑滤波器减少噪声 + const smoothData = diffData.map((value, index, arr) => { + if (index < 5) return 0; // 前几个点无法平滑 + return ( + (arr[index] + + arr[index - 1] + + arr[index - 2] + + arr[index - 3] + + arr[index - 4]) / + 5 + ); + }); + + // 阈值处理检测QRS波 + const threshold = 0.6; // 自定义阈值(需要根据实际情况调整) + smoothData.forEach((value, index) => { + if (Math.abs(value) > threshold) { + filteredData.push(value); // 保留QRS特征 + } else { + filteredData.push(0); // 非特征部分置零 + } + }); + + return filteredData; + }, + subscribeAndReceiveData() { + if (this.data.isNotifyBLEC) { + // 监听数据特征值变化 + wx.onBLECharacteristicValueChange((res) => { + // console.log('------------------------', res) + if (res.characteristicId === this.data.ecgCharacteristicId) { + const dataView = new DataView(res.value); + const hexStr = this.uint8ArrayToHex(new Uint8Array(res.value)) + // console.log("心电报文:", hexStr); + // console.log("心电报文View:", dataView); + // 获取下标 9-10 的字节 (dataType) + let cmdType = dataView.getUint16(8); + if (cmdType == 0x01) { + // console.log("心电指令,ECG数据返回:", cmdType); + } + let dataType = dataView.getUint8(10); + // console.log("dataType:", dataType); + switch (dataType) { + case 0x00: + // console.log(this.data.arrList.join(',')) + // console.log("ECG 测试结束:"); + break; + case 0x01: + // console.log("ECG 测试开始:"); + break; + case 0x02: + var bodyData = this.uint8ArrayToHex(new Uint8Array(res.value)) + var bodyFor = [] + for (let i = 0; i <= bodyData.length; i++) { + if (bodyData[i] == ' ') { + bodyFor.push(',') + } else { + bodyFor.push(bodyData[i]) + } + } + bodyData = bodyFor.join('').split(',').splice(-8) + var BPM = parseInt(bodyData[0], 16); + var EcgProgress = parseInt(bodyData[7], 16) + // console.log("ECG 参数同步:", BPM, EcgProgress) + break; + case 0x03: + const rawData = new Uint8Array(res.value); + console.log("原始数据:",rawData) + let filterSigns = []; + if (rawData.length >= 50) { + for (let i = 0; i < 25; i++) { + let filter = ((rawData[i * 2 + 1] & 0xff) << 8) | (rawData[i * 2 + 2] & 0xff); + if (filter > 32767) { + filter = filter - 65536; + } + filterSigns.push(filter); + } + } + console.log('处理后的数据:',filterSigns) + filterSigns = filterSigns.splice(5) + this.setData({ + arrList: [...this.data.arrList, ...filterSigns], + dataQueue: this.data.dataQueue.concat(filterSigns), + totalReceivedNodes: this.data.totalReceivedNodes + filterSigns.length, + }); + + this.startDrawing(); + break; + case 0x04: + // console.log("ECG 测试状态返回:"); + break; + default: + // console.warn('未知数据类型', dataType); + break; + } + } + }); + } else { + this.notifyBLEC(); + wx.showLoading({ + title: '等待订阅中', + }) + setTimeout(() => { + wx.hideLoading() + this.subscribeAndReceiveData() + }, 5000); + } + }, + uint8ArrayToHex(array) { + return Array.from(array) + .map(byte => byte.toString(16).padStart(2, '0')) // 转换每个字节为 2 位的 16 进制,并补零 + .join(' '); // 用空格连接所有字节 + }, + /** + * 将十六进制字符串转换为十进制数组 + * @param {string} hexString - 输入的十六进制字符串,每两个字符代表一个字节 + * @returns {number[]} - 转换后的十进制数组 + */ + hexStringToDecimalArray(hexString) { + if (!hexString || typeof hexString !== "string") { + throw new Error("输入必须是有效的十六进制字符串!"); + } + + // 确保字符串长度是偶数 + if (hexString.length % 2 !== 0) { + throw new Error("十六进制字符串长度必须为偶数!"); + } + + const decimalArray = []; + for (let i = 0; i < hexString.length; i += 2) { + // 截取每两个字符并转换为十进制 + const hexByte = hexString.substr(i, 2); + const decimalValue = parseInt(hexByte, 16); + + // 检查转换结果是否有效 + if (isNaN(decimalValue)) { + throw new Error(`无效的十六进制字节:${hexByte}`); + } + + decimalArray.push(decimalValue); + } + + return decimalArray; + }, + // 将两个字节合并成一个有符号的 16 位整数 + // 将每两个字节解析为一个有符号的 16 位整数 + hexStringToSignedDecimalArray(hexString) { + if (!hexString || typeof hexString !== "string") { + throw new Error("输入必须是有效的十六进制字符串!"); + } + + // 确保字符串长度是偶数 + if (hexString.length % 2 !== 0) { + throw new Error("十六进制字符串的长度必须是偶数!"); + } + + const signedDecimalArray = []; + for (let i = 0; i < hexString.length; i += 4) { // 每次处理两个字节 + // 取出两个字节,拼接为一个 16 位整数(16 进制) + const hexByte1 = hexString.substr(i, 2); // 第一个字节 + const hexByte2 = hexString.substr(i + 2, 2); // 第二个字节 + + // 将这两个字节转为十进制数 + const int16 = (parseInt(hexByte2, 16) << 8) | parseInt(hexByte1, 16); + + // 检查是否需要转换为负数 + if (int16 >= 0x8000) { + // 转换为负数(补码转换) + signedDecimalArray.push(int16 - 0x10000); + } else { + signedDecimalArray.push(int16); + } + } + + return signedDecimalArray; + }, + // ============================= + drawGridNews(ctx) { + const { + canvasWidth, + canvasHeight, + } = this.data; + var width = 1000 + var height = canvasHeight + var gridWidth = 5 + const hLineNum = parseInt(height / gridWidth); + const vLineNum = parseInt(width / gridWidth); + // console.log(hLineNum, vLineNum) + ctx.setLineWidth(1); + ctx.setStrokeStyle("#ccc"); + + // 横线 + for (let i = 0; i < hLineNum; i++) { + if (i === 0 || i % 5 !== 0) { + ctx.beginPath(); + ctx.moveTo(0, i * gridWidth); + ctx.lineTo(width, i * gridWidth); + ctx.stroke(); + } + } + + // 竖线 + for (let i = 0; i < vLineNum; i++) { + if (i === 0 || i % 5 !== 0) { + ctx.beginPath(); + ctx.moveTo(i * gridWidth, 0); + ctx.lineTo(i * gridWidth, height); + ctx.stroke(); + } + } + + // 粗线 + ctx.setStrokeStyle("#9B9B9B"); + for (let i = 5; i <= vLineNum; i += 5) { + ctx.beginPath(); + ctx.moveTo(i * gridWidth, 0); + ctx.lineTo(i * gridWidth, height); + ctx.stroke(); + } + for (let i = 5; i <= hLineNum; i += 5) { + ctx.beginPath(); + ctx.moveTo(0, i * gridWidth); + ctx.lineTo(width, i * gridWidth); + ctx.stroke(); + } + }, + drawEcgSegment(canvasId, index) { + const { + arrList, // 原始数据 + pointsPerCanvas, // 每个 Canvas 显示的点数 + canvasHeight, // 画布高度 + maxLevel, // 最大电平值 + minLevel // 最小电平值 + } = this.data; + + // 确定当前分片的起始和结束索引 + const startIndex = index * pointsPerCanvas; + const endIndex = Math.min(startIndex + pointsPerCanvas, arrList.length); + const segmentData = arrList.slice(startIndex, endIndex); // 取当前分片的数据 + + // 水平缩放比例 (每点的水平间隔) + const step = this.data.canvasWidth / pointsPerCanvas; + + // 垂直缩放比例 + const scaleY = canvasHeight / (maxLevel - minLevel); + + // 获取 Canvas Context + const ctx = wx.createCanvasContext(canvasId, this); + + // 清空画布 + ctx.clearRect(0, 0, this.data.canvasWidth, canvasHeight); + + // 绘制网格 + this.drawGridNews(ctx); + + // 绘制心电图 + ctx.setStrokeStyle('red'); + ctx.setLineWidth(1); + ctx.beginPath(); + + segmentData.forEach((value, i) => { + const x = i * step; + const y = canvasHeight - (value - minLevel) * scaleY; + + if (i === 0) { + ctx.moveTo(x, y); + } else { + const prevX = (i - 1) * step; + const prevY = canvasHeight - (segmentData[i - 1] - minLevel) * scaleY; + + // 使用二次贝塞尔曲线平滑连接点 + const midX = (prevX + x) / 2; + const midY = (prevY + y) / 2; + ctx.quadraticCurveTo(prevX, prevY, midX, midY); + } + }); + + ctx.stroke(); + ctx.draw(); + }, + drawFullECG() { + const { + arrList, + pointsPerCanvas + } = this.data; + const totalLength = arrList.length; + const canvasCount = Math.ceil(totalLength / pointsPerCanvas); // 计算需要的 Canvas 数量 + + // 生成 Canvas 列表 + const canvasList = Array.from({ + length: canvasCount + }, (_, index) => ({ + canvasId: `ecgCanvas-${index}`, + })); + this.setData({ + canvasList + }, () => { + // // 循环绘制每个 Canvas + canvasList.forEach((item, index) => { + this.drawEcgSegment(item.canvasId, index); + }); + }); + + + + + + + + + + + }, + // =============================================== + // 绘制并保存 canvas 为图片 + saveCanvasAsImage() { + const that = this; + const { + canvasList, + canvasWidth, + canvasHeight + } = that.data; + + // 创建一个目标 canvas + const targetCanvas = wx.createCanvasContext('targetCanvas', this); + + // 设置目标 canvas 宽度和高度 + const targetWidth = canvasList.length * 1000; + const targetHeight = 200; // 假设每个 canvas 高度一致 + + // 将目标 canvas 的宽高设置好 + console.log(targetCanvas,'-------------------') + // targetCanvas.canvas.width = targetWidth; + // targetCanvas.canvas.height = targetHeight; + this.setData({ + xuanranCss:{ + width:targetWidth, + height:targetHeight + } + }) + + // 循环通过每个 canvas 将图像拼接到目标 canvas + canvasList.forEach((item, index) => { + // 获取每个 canvas 的上下文并将其转为图片 + wx.canvasToTempFilePath({ + canvasId: item.canvasId, + success(res) { + // 每次拿到一个 canvas 的图片路径后,画到目标 canvas 上 + targetCanvas.drawImage(res.tempFilePath, index * canvasWidth, 0, canvasWidth, canvasHeight); + + // 如果是最后一个 canvas,就绘制完毕并导出图片 + if (index === canvasList.length - 1) { + targetCanvas.draw(false, () => { + // 合成完成后导出最终拼接的图像 + wx.canvasToTempFilePath({ + canvasId: 'targetCanvas', + success(res) { + // 这里可以获取到拼接后的图片路径 + console.log('拼接后的图片路径:', res.tempFilePath); + // 可以在页面中展示拼接后的图片 + that.setData({ + combinedImagePath: res.tempFilePath + }); + const tempFilePath = res.tempFilePath; + + // 选择保存到相册 + wx.saveImageToPhotosAlbum({ + filePath: tempFilePath, + success: (saveRes) => { + wx.showToast({ + title: '保存成功', + icon: 'success', + }); + }, + fail: (saveErr) => { + wx.showToast({ + title: '保存失败', + icon: 'none', + }); + console.error('保存失败', saveErr); + }, + }); + }, + fail(error) { + console.log('导出拼接图失败', error); + } + }); + }); + } + }, + fail(error) { + console.log('转换 canvas 为临时文件失败', error); + } + }); + }); + + }, + +}); \ No newline at end of file diff --git a/ecg/index.json b/ecg/index.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/ecg/index.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/ecg/index.wxml b/ecg/index.wxml new file mode 100644 index 0000000..5b46abd --- /dev/null +++ b/ecg/index.wxml @@ -0,0 +1,24 @@ + + + + + + + + + 总结点:{{totalReceivedNodes}} + 消费节点:{{totalConsumedNodes}} + + + + + + + + + + \ No newline at end of file diff --git a/ecg/index.wxss b/ecg/index.wxss new file mode 100644 index 0000000..fdbeaf8 --- /dev/null +++ b/ecg/index.wxss @@ -0,0 +1,7 @@ +.container { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; + } + \ No newline at end of file diff --git a/ecg2/index.js b/ecg2/index.js new file mode 100644 index 0000000..8e860f2 --- /dev/null +++ b/ecg2/index.js @@ -0,0 +1,157 @@ +Page({ + data: { + gridWidth: 5, + width: 800, + height: 300, + }, + onReady() { + // 使用 wx.createCanvasContext 创建绘图上下文 + const backgroundCtx = wx.createCanvasContext('background'); + console.log(backgroundCtx) + this.drawGrid(backgroundCtx); + backgroundCtx.draw(); // 必须调用 draw 方法将绘制提交到 canvas 上 + + // 启动波形绘制 + const lineCtx = wx.createCanvasContext('line'); + this.initializeWaves(lineCtx); + }, + drawGrid(ctx) { + const { width, height, gridWidth } = this.data; + const hLineNum = parseInt(height / gridWidth); + const vLineNum = parseInt(width / gridWidth); + + ctx.setLineWidth(1); + ctx.setStrokeStyle("#ffbebe"); + + // 横线 + for (let i = 0; i < hLineNum; i++) { + if (i === 0 || i % 5 !== 0) { + ctx.beginPath(); + ctx.moveTo(0, i * gridWidth); + ctx.lineTo(width, i * gridWidth); + ctx.stroke(); + } + } + + // 竖线 + for (let i = 0; i < vLineNum; i++) { + if (i === 0 || i % 5 !== 0) { + ctx.beginPath(); + ctx.moveTo(i * gridWidth, 0); + ctx.lineTo(i * gridWidth, height); + ctx.stroke(); + } + } + + // 粗线 + ctx.setStrokeStyle("#FF7F50"); + for (let i = 5; i <= vLineNum; i += 5) { + ctx.beginPath(); + ctx.moveTo(i * gridWidth, 0); + ctx.lineTo(i * gridWidth, height); + ctx.stroke(); + } + for (let i = 5; i <= hLineNum; i += 5) { + ctx.beginPath(); + ctx.moveTo(0, i * gridWidth); + ctx.lineTo(width, i * gridWidth); + ctx.stroke(); + } + }, + initializeWaves(ctx) { + // 配置波形参数 + const waveViews = [ + { + frameSize: 250, + yMax: 250, + y_offset: 0, + step: 10, + speedRatio: 1, + strokeStyle: '#00ff00', + }, + { + frameSize: 125, + yMax: 250, + y_offset: 100, + step: 10, + speedRatio: 0.5, + strokeStyle: '#00ff00', + }, + { + frameSize: 60, + yMax: 250, + y_offset: 200, + step: 4, + speedRatio: 1, + strokeStyle: '#00F5FF', + }, + ]; + + // 初始化波形实例并启动动画 + waveViews.forEach((config) => { + const wave = new this.WaveView(ctx, config); + wave.loop(); + }); + }, + WaveView(ctx, { frameSize, yMax, y_offset, step, speedRatio, strokeStyle }) { + this.ctx = ctx; + this.currentX = 0; + this.currentY = 0; + this.lastX = 0; + this.lastY = 0; + this.step = step; + this.yMax = yMax; + this.y_offset = y_offset; + this.queue = []; + this.strokeStyle = strokeStyle; + this.speedRatio = speedRatio; + this.itemHeight = 100; + this.clearGap = 20; + this.drawInterval = Math.floor((1 / frameSize) * 1000 * step); + + this.draw = () => { + ctx.beginPath(); + ctx.setStrokeStyle(this.strokeStyle); + + // 清除部分画布,避免图像重叠 + if (this.lastX === 0) { + ctx.clearRect(this.currentX - 2, this.y_offset, this.clearGap, this.itemHeight); + } else { + ctx.clearRect(this.currentX + this.lastX, this.y_offset, this.clearGap, this.itemHeight); + } + + // 绘制波形 + for (let i = 0; i < this.step; i++) { + if (this.queue.length === 0) { + this.currentY = this.itemHeight / 2; + } else { + this.currentY = (-1.0 * this.queue.shift()) / this.yMax * this.itemHeight + this.itemHeight; + } + + ctx.moveTo(this.currentX + this.lastX, this.y_offset + this.lastY); + ctx.lineTo(this.currentX, this.y_offset + this.currentY); + + this.lastX = this.currentX; + this.lastY = this.currentY; + this.currentX += (this.step * 25 * this.speedRatio) / frameSize; + + if (this.currentX >= 800 - 25) { + this.currentX = 0; + this.lastX = 0; + } + } + ctx.stroke(); + ctx.draw(); // 必须提交绘制 + }; + + this.loop = () => { + this.draw(); + setTimeout(this.loop, this.drawInterval); // 循环调用 + }; + + this.addData = (arr) => { + this.queue.push(...arr); // 添加数据到队列 + }; + }, + }); + \ No newline at end of file diff --git a/ecg2/index.json b/ecg2/index.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/ecg2/index.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/ecg2/index.wxml b/ecg2/index.wxml new file mode 100644 index 0000000..54a9590 --- /dev/null +++ b/ecg2/index.wxml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ecg2/index.wxss b/ecg2/index.wxss new file mode 100644 index 0000000..1a386ff --- /dev/null +++ b/ecg2/index.wxss @@ -0,0 +1,8 @@ +.boack { + position: absolute; + left: 0; + top: 10rpx; + width: 800rpx; + height: 300rpx; + } + \ No newline at end of file diff --git a/index/index.js b/index/index.js new file mode 100644 index 0000000..2afb477 --- /dev/null +++ b/index/index.js @@ -0,0 +1,558 @@ +const SendCommandUtil = require('../utils/SendCommandUtil.js'); // 引用 发送指令工具类 +const DataPacket = require('../utils/DataPacket.js'); +const ParseDataPacket = require('../utils/ParseDataPacket.js'); +const DynamicArrayBuffer = require('../utils/DynamicArrayBuffer.js'); +const CommonUtil = require('../utils/CommonUtil.js'); +const HealthDataCache = require('../utils/HealthDataCache.js'); + + +Page({ + // 数据定义区域,存储蓝牙设备 ID、服务 ID 和特征 ID + data: { + deviceId: '', // 存储蓝牙设备的ID(IOS是UUID ,安卓是MAC) + serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',// 存储蓝牙设备的服务 UUID + notifyCharacteristicId:'0000FFF7-0000-1000-8000-00805F9B34FB',// 通知特征 + initIndex: 0, + initTime: Math.floor(new Date().getTime() / 1000), + // 定义蓝牙数据缓存 + sleepDataBuffer: new DynamicArrayBuffer(), // 创建缓存对象 + isDataComplete: false, // 判断数据是否上传完毕 + sleepList:[], + }, + + // 页面加载时触发的函数,用于初始化蓝牙适配器 + onLoad() { + wx.getLocation({ + type: 'gcj02', + success(res) { + console.log("开启位置授权:", res) + }, + fail(err) { + console.error('获取位置失败', err); + } + }); + wx.openBluetoothAdapter({ // 开启蓝牙适配器 + success: (res) => { + console.log('蓝牙适配器初始化成功'); // 蓝牙适配器初始化成功的回调 + this.startBluetoothDevicesDiscovery(); // 开始扫描附近的蓝牙设备 + }, + fail: (err) => { + console.error('蓝牙适配器初始化失败', err); // 蓝牙适配器初始化失败的回调 + } + }); + }, + + getLocation(){ + wx.getLocation({ + type: 'gcj02', + success(res) { + console.log("开启位置授权:", res) + }, + fail(err) { + console.error('获取位置失败', err); + } + }); + }, + // 扫描蓝牙设备 + startBluetoothDevicesDiscovery() { + wx.startBluetoothDevicesDiscovery({ // 启动设备扫描 + allowDuplicatesKey: false, // 是否允许重复设备 + success: (res) => { + console.log('开始扫描附近的蓝牙设备'); // 扫描成功回调 + this.getBluetoothDevices(); // 调用函数获取蓝牙设备列表 + }, + fail: (res) => { + console.log('开始扫描附近的蓝牙设备', res); + }, + }); + }, + + // 获取附近的蓝牙设备列表,并尝试连接到目标设备 + getBluetoothDevices() { + wx.onBluetoothDeviceFound((res) => { + // 过滤出名称以 "GJ-SH-01" 开头的设备 + const targetDevices = res.devices.filter(device => device.name && device.name.startsWith("J2203")); + + // 遍历并连接符合条件的设备 + targetDevices.forEach(device => { + console.log("找到符合规则的蓝牙设备:", device); + const hexStr = this.uint8ArrayToMac(new Uint8Array(device.advertisData)) + console.log("advertisData: ",hexStr) + // 连接指定设备 + if(device.deviceId=="03:02:02:07:65:98"){ + this.connectToDevice(device.deviceId); + } + }); + }); + }, + + // 连接指定的蓝牙设备 + connectToDevice(deviceId) { + wx.createBLEConnection({ // 建立与蓝牙设备的连接 + deviceId, // 设备的 ID + success: () => { + this.setData({ deviceId }); // 将设备 ID 保存到 data 中 + console.log('蓝牙设备连接成功'); // 连接成功的回调 + // 获取服务IDs和特征值 + this.getCharacteristicId() + + }, + fail: (err) => { + console.error('蓝牙设备连接失败', err); // 连接失败的回调 + } + }); + }, + + // 获取服务下的特征 UUID + getCharacteristicId() { + // 获取设备服务列表 + wx.getBLEDeviceServices({ + deviceId: this.data.deviceId, + success: (res) => { + res.services.forEach(service => { + // 获取每个服务的特征值 + wx.getBLEDeviceCharacteristics({ + deviceId: this.data.deviceId, + serviceId: service.uuid, + success: (res) => { + res.characteristics.forEach(characteristic => { + if(characteristic.uuid.startsWith("0000FFF7")){ + // 开始订阅特征 + this.subscribeAndReceiveData() + } + }); + } + }); + }); + } + }); + }, + // 连接指定的蓝牙设备 + connectToDevice(deviceId) { + wx.createBLEConnection({ // 建立与蓝牙设备的连接 + deviceId, // 设备的 ID + success: () => { + this.setData({ + deviceId + }); // 将设备 ID 保存到 data 中 + console.log('蓝牙设备连接成功'); // 连接成功的回调 + // 获取服务IDs和特征值 + this.getCharacteristicId() + }, + fail: (err) => { + console.error('蓝牙设备连接失败', err); // 连接失败的回调 + } + }); + }, + // 获取数据(健康数据、运动数据) + getData() { + SendCommandUtil.getHealthData(this.data.deviceId); + }, + + // 订阅 并 接收数据 + subscribeAndReceiveData() { + wx.notifyBLECharacteristicValueChange ({ + deviceId: this.data.deviceId, // 蓝牙设备 ID + serviceId: this.data.serviceId, // 蓝牙服务 UUID + characteristicId: this.data.notifyCharacteristicId, // 健康数据 + state: true, + success: (res) => { + console.log('订阅手表向手机端发送数据事件', res); + }, + fail: (err) => { + console.error('手表向手机端发送数据订阅失败', err); + }, + }); + + wx.onBLECharacteristicValueChange((res) => { + const hexStr = this.uint8ArrayToHex(new Uint8Array(res.value)) + // console.log("收到手表端发送的数据>>>>>>:",hexStr); + const dataView = new DataView(res.value); + // 获取第一个字节 + let cmdType = dataView.getUint8(0); + console.log("cmdType",cmdType); + switch (cmdType) { + case 0x01: + console.log("同步时间成功"); + break; + case 0x02: + console.log("同步用户信息成功"); + break; + case 0x04: + let basicInfo = ParseDataPacket.parseWatchBasic(dataView,0); + console.log("手环基本信息:",basicInfo); + break; + case 0x13: + let power = dataView.getUint8(1); + console.log("获得电量成功:",power); + break; + case 0x23: + wx.showToast({ + title: '设置成功', // 提示的内容 + icon: 'success', // 图标,可选值为 'success', 'loading', 'none' + duration: 2000 // 提示的持续时间,单位为毫秒 + }); + break; + case 0x25: + wx.showToast({ + title: '设置成功', + icon: 'success', + duration: 2000 + }); + break; + case 0x26: + let sedentaryInfo = ParseDataPacket.parseSedentary(dataView,0); + console.log("久坐信息:",sedentaryInfo); + break; + case 0x4B: + let targetInfo = ParseDataPacket.parseStepTarget(dataView,0); + console.log("运动目标信息:",targetInfo); + break; + case 0x51: + let stepList = ParseDataPacket.parseStepList(dataView,0); + console.log("步数总数据:",stepList); + break; + case 0x52: + let stepDetailList = ParseDataPacket.parseStepDetailList(dataView,0); + console.log("步数详情数据:",stepDetailList); + // 调用后台接口 todo .... + + + // 更新缓存 + HealthDataCache.updateStepData(stepDetailList[0].startTime); + break; + case 0x55: + let heartRateList = ParseDataPacket.parseDataList(dataView,0); + let respList = this.getRespiratoryList(heartRateList); + console.log("心率数据:",heartRateList); + // console.log("呼吸率数据:",respList); + // 调用后台接口 todo .... + + // 更新缓存 + HealthDataCache.updateHeartRateData(heartRateList[0].time); + + break; + case 0x56: + let parseBloodList = ParseDataPacket.parseBloodPressureList(dataView,0); + console.log("血压数据:",parseBloodList); + // 调用后台接口 todo .... + + // 更新缓存 + HealthDataCache.updateBloodPressureData(parseBloodList[0].time); + break; + case 0x57: + let alarmClockList = ParseDataPacket.parseAlarmClockList(dataView,0); + console.log("获取闹钟数据成功:",alarmClockList); + break; + case 0x66: + let bloodOxygenList = ParseDataPacket.parseDataList(dataView,0); + console.log("血氧数据:",bloodOxygenList); + // 调用后台接口 todo .... + + // 更新缓存 + HealthDataCache.updateBloodOxygenData(bloodOxygenList[0].time); + break; + case 0x65: + let tempList = ParseDataPacket.parseTempList(dataView,0); + // 目前返回的包括环境温度,后续固件改成只采集佩戴情况下的体温,暂时过滤掉 oneValue 小于 35.0 的数据 + const filteredData = tempList.filter(item => item.oneValue >= 35.0); + console.log("体温数据:",tempList); + + if(filteredData.length > 0){ + // todo 调用后端接口 + + // 更新缓存 + HealthDataCache.updateTemperatureData(filteredData[0].time); + } + break; + case 0x53: + let id = dataView.getUint8(1); + // 睡眠数据结束符 + if(dataView.byteLength == 2 && id == 0xff){ + let list = this.data.sleepList; + let arr = CommonUtil.assembleSleepData(list); + console.log("接口需要的睡眠数据:",arr); + + // // 更新缓存 + // HealthDataCache.updateSleepData(list[0].time); + + // 调用接口传睡眠后,清空数据 + this.setData({ + sleepList: [] + }); + } else { + let sleepList = ParseDataPacket.parseSleepList(dataView,0); + // 排序,minute 越大排前面 + sleepList.sort((a, b) => { + return Number(b.minute) - Number(a.minute); // b.minute > a.minute 排前面 + }); + console.log("排序后数据:",sleepList) + // 获取当前已存在的 sleepList(如果有) + let currentSleepList = this.data.sleepList || []; + + // 将新的 sleepList 追加到现有的 sleepList 中 + let updatedSleepList = currentSleepList.concat(sleepList); + + // 更新 data 中的 sleepList + this.setData({ + sleepList: updatedSleepList + }); + + } + break; + default: + console.warn('未知数据类型', cmdType); + break; + } + }); + }, + + init(){ + // 同步时间 + const setTimePacket = DataPacket.generateSetTimePacket(); + SendCommandUtil.sendPackage(this.data.deviceId,setTimePacket); + + // 同步用户性别、身高、体重、年龄 + const userInfoPacket = DataPacket.generateSetUserInfoPacket(1,30,168,55); + SendCommandUtil.sendPackage(this.data.deviceId,userInfoPacket); + + }, + + getPower(){ + // 获取电量 + const batteryPacket = DataPacket.generateBatteryCheckPacket(); + SendCommandUtil.sendPackage(this.data.deviceId,batteryPacket); + }, + + getStep(){ + // 获取步数(按天的总步数) + const stepPacket = DataPacket.generateReadStepTotalDataPacket(); + SendCommandUtil.sendPackage(this.data.deviceId,stepPacket); + }, + + sendHealthCommand(){ + // 指令依次是:心率,血氧、血压、体温、睡眠、步数 + // 0x55,0x66,0x56,0x65,0x53,0x52 + const commands = [0x55,0x66,0x56,0x65,0x53,0x52]; + // 递归发送命令并加上 5 秒延迟 + function sendNextCommand(index = 0) { + if (index >= commands.length) return; // 所有命令发送完毕,结束 + const cmd = commands[index]; + const packet = DataPacket.generateReadDataPacket(cmd); + console.log("hexStr", this.uint8ArrayToHex(packet)); + + // 发送命令包 + SendCommandUtil.sendPackage(this.data.deviceId, packet); + + // 每发送完一个命令后,等待 3 秒继续发送下一个 + setTimeout(() => { + sendNextCommand.call(this, index + 1); + }, 3500); // 延迟 3.5 秒 + } + + // 开始发送第一个命令,并保持 `this` 上下文 + sendNextCommand.call(this,0); + + }, + + // 获取健康数据 + getHealthData(){ + + // 睡眠数据 + // const sleepPacket = DataPacket.generateReadSleepDataPacket(); + // SendCommandUtil.sendPackage(this.data.deviceId,sleepPacket); + // 获取当前时间 + let currentTime = Math.floor(new Date().getTime() / 1000); + // 如果是第一次调用,则不判断时间,如果是第二次调用,要判断本次调用与上次是否间隔18秒(因手表机制问题,不能频繁发送指令给手表端) + console.log(this.data.initTime,this.data.initIndex) + if(this.data.initIndex === 0){ + this.sendHealthCommand(); + this.setData({ + initIndex: this.data.initIndex + 1, + initTime: currentTime + }) + } else { + // 初始时间 + let initTime = this.data.initTime + let timeDifference = currentTime - initTime; + if(this.data.initIndex > 0 && timeDifference > 18){ + console.log(timeDifference,this.data.initIndex) + this.setData({ + initIndex: this.data.initIndex + 1, + initTime: currentTime + }) + this.sendHealthCommand(); + } + } + + }, + + + /** + * 佩戴管理设置 + */ + wearManageSetting() { + // 测试用例 > 手表基础信息设置(抬腕亮屏,屏幕亮度)------------------------------------------------------------ + // let basicPacket = DataPacket.generateWatchBasicPacket(0,0); + // let hex = this.uint8ArrayToHex(basicPacket); + // console.log('发送抬腕亮屏,屏幕亮度指令:', hex); + // SendCommandUtil.sendPackage(this.data.deviceId,basicPacket); + + + // 测试用例 > 闹钟、吃药、喝水------------------------------------------------------------------------------ + // const week = [0,1,1,1,1,1,0];// 表示周天、周一、周二、周三、周四、周五、周六 + // let binaryWeek = CommonUtil.getWeekBinary(week); + // const alarms = [ + // { + // id: 0, // 闹钟编号 + // enabled: true, // 是否启用 + // type: 1, // 闹钟类型:1:闹钟,2:吃药提示,3:喝水提示 + // hour: 14, // 小时 + // minute: 11, // 分钟 + // weekEnable: binaryWeek, // 星期使能:每天都启用 + // text: "闹钟", // 文本内容 + // }, + // { + // id: 1, + // enabled: true, + // type: 3, + // hour: 14, + // minute: 13, + // weekEnable: 0b01111111, // 启用周一、周三、周五 + // text: "喝水", + // }, + // { + // id: 2, + // enabled: true, + // type: 2, + // hour: 10, + // minute: 28, + // weekEnable: 0b01111111, // 启用周一、周三、周五 + // text: "吃药", + // }, + // ]; + // // 这里的公式写死,不可修改 + // let maxLength = 39 * alarms.length + 2; + // let alarmClockPackets = DataPacket.generateAlarmClockPackets(maxLength,alarms); + // let hex = this.uint8ArrayToHex(alarmClockPackets); + // console.log('发送闹钟指令:', hex); + // SendCommandUtil.sendPackage(this.data.deviceId,alarmClockPackets); + + // 测试用例 > 久坐------------------------------------------------------------------------------------- + let sportTimePackets = DataPacket.generateSedentaryPacket(9, 0, 20, 0, [0,1,1,1,1,1,0], 60, 100, 1); + let hex1 = this.uint8ArrayToHex(sportTimePackets); + console.log('发送久坐指令:', hex1); + SendCommandUtil.sendPackage(this.data.deviceId,sportTimePackets); + + // 测试用例 > 运动目标------------------------------------------------------------------------------------- + let targetPacket = DataPacket.generateStepTargetPacket(5000,0,4,160,480); + let hex2 = this.uint8ArrayToHex(sportTimePackets); + console.log('发送运动目标指令:', hex2); + SendCommandUtil.sendPackage(this.data.deviceId,targetPacket); + + }, + + // 获取佩戴管理各项设置值 + wearManageGetting() { + + // 获取手环基本信息 + // let basic = DataPacket.getWatchBasic(); + // SendCommandUtil.sendPackage(this.data.deviceId,basic); + + // 获取闹钟数据(闹钟、吃药、喝水) + // let alarmClockPacket = DataPacket.getAlarmClock(); + // SendCommandUtil.sendPackage(this.data.deviceId,alarmClockPacket); + + // 获取久坐数据 + // let sedentaryPacket = DataPacket.getSedentary(); + // SendCommandUtil.sendPackage(this.data.deviceId,sedentaryPacket); + + // 获得运动目标 + // let stepTargetPacket = DataPacket.getStepTarget(); + // SendCommandUtil.sendPackage(this.data.deviceId,stepTargetPacket); + + }, + + /** + * 下发天气指令 + */ + downWeather(){ + let packet = DataPacket.generateWeatherPacket("晴","20","30","10","66","长沙市") + let hex = this.uint8ArrayToHex(packet); + console.log('发送天气指令:', hex); + SendCommandUtil.sendPackage(this.data.deviceId,packet); + }, + + removeCache(){ + wx.removeStorageSync('healthDataCache'); + this.setData({ + index1:0, + index2:0, + index3:0, + index4:0, + index5:0 + }) + }, + + // 字节转16进制 + uint8ArrayToHex(array) { + return Array.from(array) + .map(byte => byte.toString(16).padStart(2, '0')) // 转换每个字节为 2 位的 16 进制,并补零 + .join(' '); // 用空格连接所有字节 + }, + + // IOS MAC地址转换 + uint8ArrayToMac(array) { + return Array.from(array.slice(2, 8)) + .map(byte => byte.toString(16).padStart(2, '0').toUpperCase()) // 转换为两位 16 进制 + .join(':'); // 用冒号连接 + }, + /** + * 根据心率批量计算呼吸率 + * @param {解析后的心率列表} heartList + */ + getRespiratoryList(heartList){ + let respList = []; + heartList.forEach(item => { + let respValue = this.calculateRespiratory(item.oneValue); + let respItem = { + type:5, + oneValue:respValue, + time:item.time + } + respList.push(respItem); + }) + + return respList; + }, + /** + * 根据心率计算呼吸率 + * @param {心率} heartValue + */ + calculateRespiratory(heartValue){ + //心率值 + let X = heartValue; + + //呼吸率 + let Y = 0; + + //正常心率计算法 + if(X >= 50 && X <= 100){ + Y= ( ( X - 2 ) / 4 ); + + //心率大于100的计算法 + } else if ( X >100 ){ + Y= ( 25 + ( X - 100 ) / 4 ); + + //心率小于50的计算法 + } else if ( X < 50 ){ + //计算结果向下取整 + Y = Math.floor(12 - (50 - X) / 4.0); + } else { + Y = 0; + } + + return parseInt(Y); + }, + +}); \ No newline at end of file diff --git a/index/index.wxml b/index/index.wxml new file mode 100644 index 0000000..fbbed66 --- /dev/null +++ b/index/index.wxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/index/index.wxss b/index/index.wxss new file mode 100644 index 0000000..561033e --- /dev/null +++ b/index/index.wxss @@ -0,0 +1,41 @@ +page { + color: #333; +} +.devices_summary { + margin-top: 30px; + padding: 10px; + font-size: 16px; +} +.device_list { + height: 300px; + margin: 50px 5px; + margin-top: 0; + border: 1px solid #EEE; + border-radius: 5px; + width: auto; +} +.device_item { + border-bottom: 1px solid #EEE; + padding: 10px; + color: #666; +} +.device_item_hover { + background-color: rgba(0, 0, 0, .1); +} +.connected_info { + position: fixed; + bottom: 0; + width: 100%; + background-color: #F0F0F0; + padding: 10px; + padding-bottom: 20px; + margin-bottom: env(safe-area-inset-bottom); + font-size: 14px; + min-height: 100px; + box-shadow: 0px 0px 3px 0px; +} +.connected_info .operation { + position: absolute; + display: inline-block; + right: 30px; +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..58d7b51 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "es2015", + "module": "commonjs" + } +} \ No newline at end of file diff --git a/project.config.json b/project.config.json new file mode 100644 index 0000000..b5c94f9 --- /dev/null +++ b/project.config.json @@ -0,0 +1,47 @@ +{ + "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "setting": { + "urlCheck": true, + "es6": false, + "postcss": true, + "minified": true, + "newFeature": true, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "coverView": true, + "lazyloadPlaceholderEnable": false, + "preloadBackgroundData": false, + "autoAudits": false, + "uglifyFileName": false, + "uploadWithSourceMap": true, + "useIsolateContext": true, + "enhance": true, + "useMultiFrameRuntime": true, + "showShadowRootInWxmlPanel": true, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true, + "disableUseStrict": false, + "ignoreUploadUnusedFiles": true, + "minifyWXML": true, + "checkInvalidKey": true, + "useStaticServer": true, + "showES6CompileOption": false, + "useCompilerPlugins": false + }, + "compileType": "miniprogram", + "condition": {}, + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + }, + "libVersion": "3.6.4", + "packOptions": { + "ignore": [], + "include": [] + }, + "appid": "wxb761a6f5c6200ed8" +} \ No newline at end of file diff --git a/project.private.config.json b/project.private.config.json new file mode 100644 index 0000000..b65990f --- /dev/null +++ b/project.private.config.json @@ -0,0 +1,7 @@ +{ + "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "projectname": "2203_bluetooth", + "setting": { + "compileHotReLoad": true + } +} \ No newline at end of file diff --git a/sitemap.json b/sitemap.json new file mode 100644 index 0000000..ca02add --- /dev/null +++ b/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/typings/wx.d.ts b/typings/wx.d.ts new file mode 100644 index 0000000..a2ffc2b --- /dev/null +++ b/typings/wx.d.ts @@ -0,0 +1,2705 @@ +// generate time:2017-08-23 21:12:06 +// Type definitions for wx app +// Definitions by: hellopao + +/************************************************ +* * +* 微信小程序 API * +* * +************************************************/ + +interface IAnimation { + /** + * 透明度,参数范围 0~1 + */ + opacity(value: number): IAnimation; + /** + * 颜色值 + */ + backgroundColor(color: string): IAnimation; + /** + * 长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值 + */ + width(length: number): IAnimation; + /** + * 长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值 + */ + height(length: number): IAnimation; + /** + * 长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值 + */ + top(length: number): IAnimation; + /** + * 长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值 + */ + left(length: number): IAnimation; + /** + * 长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值 + */ + bottom(length: number): IAnimation; + /** + * 长度值,如果传入 Number 则默认使用 px,可传入其他自定义单位的长度值 + */ + right(length: number): IAnimation; + /** + * deg的范围-180~180,从原点顺时针旋转一个deg角度 + */ + rotate(deg: number): IAnimation; + /** + * deg的范围-180~180,在X轴旋转一个deg角度 + */ + rotateX(deg: number): IAnimation; + /** + * deg的范围-180~180,在Y轴旋转一个deg角度 + */ + rotateY(deg: number): IAnimation; + /** + * deg的范围-180~180,在Z轴旋转一个deg角度 + */ + rotateZ(deg: number): IAnimation; + /** + * 同transform-function rotate3d + */ + rotate3d(x: number, y: number, z: number, deg: number): IAnimation; + /** + * 一个参数时,表示在X轴、Y轴同时缩放sx倍数;两个参数时表示在X轴缩放sx倍数,在Y轴缩放sy倍数 + */ + scale(sx: number, sy?: number): IAnimation; + /** + * 在X轴缩放sx倍数 + */ + scaleX(sx: number): IAnimation; + /** + * 在Y轴缩放sy倍数 + */ + scaleY(sy: number): IAnimation; + /** + * 在Z轴缩放sy倍数 + */ + scaleZ(sz: number): IAnimation; + /** + * 在X轴缩放sx倍数,在Y轴缩放sy倍数,在Z轴缩放sz倍数 + */ + scale3d(sx: number, sy: number, sz: number): IAnimation; + /** + * 一个参数时,表示在X轴偏移tx,单位px;两个参数时,表示在X轴偏移tx,在Y轴偏移ty,单位px。 + */ + translate(tx: number, ty?: number): IAnimation; + /** + * 在X轴偏移tx,单位px + */ + translateX(tx: number): IAnimation; + /** + * 在Y轴偏移tx,单位px + */ + translateY(tx: number): IAnimation; + /** + * 在Z轴偏移tx,单位px + */ + translateZ(tx: number): IAnimation; + /** + * 在X轴偏移tx,在Y轴偏移ty,在Z轴偏移tz,单位px + */ + translate3d(tx: number, ty: number, tz: number): IAnimation; + /** + * 参数范围-180~180;一个参数时,Y轴坐标不变,X轴坐标延顺时针倾斜ax度;两个参数时,分别在X轴倾斜ax度,在Y轴倾斜ay度 + */ + skew(ax: number, ay?: number): IAnimation; + /** + * 参数范围-180~180;Y轴坐标不变,X轴坐标延顺时针倾斜ax度 + */ + skewX(ax: number): IAnimation; + /** + * 参数范围-180~180;X轴坐标不变,Y轴坐标延顺时针倾斜ay度 + */ + skewY(ay: number): IAnimation; + /** + * 同transform-function matrix + */ + matrix(a, b, c, d, tx, ty): IAnimation; + /** + * 同transform-function matrix3d + */ + matrix3d(): IAnimation; +} + +interface ICanvasContext { + /** + * 设置填充色, 如果没有设置 fillStyle,默认颜色为 black。 + */ + setFillStyle(color: string): void; + /** + * 设置边框颜色, 如果没有设置 fillStyle,默认颜色为 black。 + */ + setStrokeStyle(color: string): void; + /** + * 设置阴影 + */ + setShadow(offsetX: number, offsetY: number, blur: number, color: string): void; + /** + * 创建一个线性的渐变颜色。需要使用 addColorStop() 来指定渐变点,至少要两个。 + */ + createLinearGradient(x0: number, y0: number, x1: number, y1: number): void; + /** + * 创建一个圆形的渐变颜色。 起点在圆心,终点在圆环。 需要使用 addColorStop() 来指定渐变点,至少要两个。 + */ + createCircularGradient(x: number, y: number, r: number): void; + /** + * 创建一个颜色的渐变点。小于最小 stop 的部分会按最小 stop 的 color 来渲染,大于最大 stop 的部分会按最大 stop 的 color 来渲染。需要使用 addColorStop() 来指定渐变点,至少要两个。 + */ + addColorStop(stop: number, color: string): void; + /** + * 设置线条端点的样式 + */ + setLineCap(lineCap: 'butt' | 'round' | 'square'): void; + /** + * 设置两线相交处的样式 + */ + setLineJoin(lineJoin: 'bevel' | 'round' | 'miter'): void; + /** + * 设置线条宽度 + */ + setLineWidth(lineWidth: number): void; + /** + * 设置最大倾斜 + */ + setMiterLimit(miterLimit: number): void; + /** + * 添加一个矩形路径到当前路径。 + */ + rect(x: number, y: number, width: number, height: number): void; + /** + * 填充一个矩形。用 setFillStyle() 设置矩形的填充色,如果没设置默认是黑色。 + */ + fillRect(x: number, y: number, width: number, height: number): void; + /** + * 一个矩形(非填充)。用 setFillStroke() 设置矩形线条的颜色,如果没设置默认是黑色。 + */ + strokeRect(x: number, y: number, width: number, height: number): void; + /** + * 在给定的矩形区域内,清除画布上的像素 + */ + clearRect(x: number, y: number, width: number, height: number): void; + /** + * 对当前路径进行填充 + */ + fill(): void; + /** + * 对当前路径进行描边 + */ + stroke(): void; + /** + * 开始一个路径 + */ + beginPath(): void; + /** + * 关闭一个路径 + */ + closePath(): void; + /** + * 把路径移动到画布中的指定点,但不创建线条。 + */ + moveTo(x: number, y: number): void; + /** + * 添加一个新点,然后在画布中创建从该点到最后指定点的线条。 + */ + lineTo(x: number, y: number): void; + /** + * 添加一个弧形路径到当前路径,顺时针绘制。 + */ + arc(x: number, y: number, radius: number, startAngle: number, sweepAngle: number): void; + /** + * 创建二次方贝塞尔曲线 + */ + quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void; + /** + * 创建三次方贝塞尔曲线 + */ + bezierCurveTo(cpx1: number, cpy1: number, cpx2: number, cpy2: number, x: number, y: number): void; + /** + * 对横纵坐标进行缩放 + */ + scale(scaleWidth: number/**横坐标缩放的倍数1 = 100%,0.5 = 50%,2 = 200%,依次类 */, scaleHeight: number/** 纵坐标轴缩放的倍数1 = 100%,0.5 = 50%,2 = 200%,依次类 */): void; + /** + * 对坐标轴进行顺时针旋转 + */ + rotate(deg: number/**degrees * Math.PI/180;degrees范围为0~360;旋转角度,以弧度计 */): void; + /** + * 对坐标原点进行缩放 + */ + translate(x: number/**水平坐标平移量 */, y: number/**竖直坐标平移量 */): void; + /** + * 在画布上绘制被填充的文本 + */ + fillText(text: string, x: number, y: number): void; + /** + * 设置字体大小 + */ + setFontSize(fontSize: number): void; + /** + * 在画布上绘制图像 + */ + drawImage(imageResource: string, x: number, y: number, width: number, height: number): void; + /** + * 设置全局画笔透明度。 + */ + setGlobalAlpha(alpha: number): void; + /** + * 保存当前坐标轴的缩放、旋转、平移信息 + */ + save(): void; + /** + * 恢复之前保存过的坐标轴的缩放、旋转、平移信息 + */ + restore(): void; + /** + * 进行绘图 + */ + draw(): void; +} + +interface IAudioContext { + /** + * 播放 + */ + play: () => void; + /** + * 暂停 + */ + pause: () => void; + /** + * 跳转到指定位置,单位 s + */ + seek: (position: number) => void; +} + +interface IVideoContext { + /** + * 播放 + */ + play: () => void; + /** + * 暂停 + */ + pause: () => void; + /** + * 跳转到指定位置,单位 s + */ + seek: (position: number) => void; + /** + * 发送弹幕,danmu 包含两个属性 text, color。 + */ + sendDanmu: (danmu: {text: string; color: string;}) => void; +} + +interface IMapContext { + /** + * 获取当前地图中心的经纬度,返回的是 gcj02 坐标系,可以用于 wx.openLocation + */ + getCenterLocation: (obj: { + /** + * 接口调用成功的回调函数 ,res = { longitude: "经度", latitude: "纬度"} + */ + success?: (res: {longitude: string; latitude: string}) => void; + /** + * 接口调用失败的回调函数 + */ + fail?: () => void; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: () => void; + }) => void; + /** + * 将地图中心移动到当前定位点,需要配合map组件的show-location使用 + */ + moveToLocation: () => void; +} + +interface Application { + setData: (obj: any) => void; +} + +interface AppConstructor { + new (): Application; + (opts: { + /** + * 生命周期函数--监听小程序初始化 + */ + onLaunch?: () => void; + /** + * 生命周期函数--监听小程序显示 + */ + onShow?: () => void; + /** + * 生命周期函数--监听小程序隐藏 + */ + onHide?: () => void; + + [key: string]: any; + }): Application; +} + +declare var App: AppConstructor; +declare function getApp(): Application; + +declare function getCurrentPages(): Page[]; + +interface Page { + setData: (obj: any) => void; +} + +interface PageConstructor { + new (): Page; + (opts: { + /** + * 页面的初始数据 + */ + data?: any; + /** + * 页面的初始数据 + */ + onLoad?: () => void; + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady?: () => void; + /** + * 生命周期函数--监听页面显示 + */ + onShow?: () => void; + /** + * 生命周期函数--监听页面隐藏 + */ + onHide?: () => void; + /** + * 生命周期函数--监听页面卸载 + */ + onUnload?: () => void; + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefreash?: () => void; + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom?: () => void; + /** + * 用户点击右上角分享 + */ + onShareAppMessage?: () => { + /** + * 分享标题, 默认值当前小程序名称 + */ + title: string; + /** + * 分享描述, 默认值当前小程序名称 + */ + desc: string; + /** + * 分享路径 默认值当前页面 path ,必须是以 / 开头的完整路径 + */ + path: string; + }; + + [key: string]: any; + }): Page; +} + +declare var Page: PageConstructor; + +declare var wx: { + // # 网络 # + + request(obj: { + /** + * 开发者服务器接口地址 + */ + url: string; + /** + * 请求的参数 + */ + data?: any | string; + /** + * 设置请求的 header , header 中不能设置 Referer + */ + header?: any; + /** + * 默认为 GET,有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT + */ + method?: string; + /** + * 默认为 json。如果设置了 dataType 为 json,则会尝试对响应的数据做一次 JSON.parse + */ + dataType?: string; + /** + * 收到开发者服务成功返回的回调函数,res = {data: '开发者服务器返回的内容'} + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 将本地资源上传到开发者服务器。如页面通过 wx.chooseImage 等接口获取到一个本地资源的临时文件路径后,可通过此接口将本地资源上传到指定服务器。客户端发起一个 HTTPS POST 请求,其中 content-type 为 multipart/form-data 。 + */ + uploadFile(obj: { + /** + * 开发者服务器 url + */ + url: string; + /** + * 要上传文件资源的路径 + */ + filePath: string; + /** + * 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容 + */ + name: string; + /** + * HTTP 请求 Header , header 中不能设置 Referer + */ + header?: any; + /** + * HTTP 请求中其他额外的 form data + */ + formData?: any; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 下载文件资源到本地。客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。 + */ + downloadFile(obj: { + /** + * 下载资源的 url + */ + url: string; + /** + * HTTP 请求 Header + */ + header?: any; + /** + * 下载成功后以 tempFilePath 的形式传给页面,res = {tempFilePath: '文件的临时路径'} + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 创建一个 WebSocket 连接;一个微信小程序同时只能有一个 WebSocket 连接,如果当前已存在一个 WebSocket 连接,会自动关闭该连接,并重新创建一个 WebSocket 连接。 + */ + connectSocket(obj: { + /** + * 开发者服务器接口地址,必须是 wss 协议,且域名必须是后台配置的合法域名 + */ + url: string; + /** + * 请求的数据 + */ + data?: any; + /** + * HTTP Header , header 中不能设置 Referer + */ + header?: any; + /** + * 默认是GET,有效值: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT + */ + method?: string; + /** + * 子协议数组 + */ + protocols?: string[]; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 监听WebSocket连接打开事件。 + */ + onSocketOpen(callback: Function): void; + + /** + * 监听WebSocket错误。 + */ + onSocketError(callback: Function): void; + + /** + * 通过 WebSocket 连接发送数据,需要先 wx.connectSocket,并在 wx.onSocketOpen 回调之后才能发送。 + */ + sendSocketMessage(obj: { + /** + * 需要发送的内容 + */ + data: undefined; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 监听WebSocket接受到服务器的消息事件。 + */ + onSocketMessage(callback: Function): void; + + /** + * 关闭WebSocket连接。 + */ + closeSocket(obj: { + /** + * 一个数字值表示关闭连接的状态号,表示连接被关闭的原因。如果这个参数没有被指定,默认的取值是1000 (表示正常连接关闭) + */ + code?: number; + /** + * 一个可读的字符串,表示连接被关闭的原因。这个字符串必须是不长于123字节的UTF-8 文本(不是字符) + */ + reason?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 监听WebSocket关闭。 + */ + onSocketClose(callback: Function): void; + + // # 媒体 # + + /** + * 从本地相册选择图片或使用相机拍照。 + */ + chooseImage(obj: { + /** + * 最多可以选择的图片张数,默认9 + */ + count?: number; + /** + * original 原图,compressed 压缩图,默认二者都有 + */ + sizeType?: string[]; + /** + * album 从相册选图,camera 使用相机,默认二者都有 + */ + sourceType?: string[]; + /** + * 成功则返回图片的本地文件路径列表 tempFilePaths + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 预览图片。 + */ + previewImage(obj: { + /** + * 当前显示图片的链接,不填则默认为 urls 的第一张 + */ + current?: string; + /** + * 需要预览的图片链接列表 + */ + urls: string[]; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 获取图片信息 + */ + getImageInfo(obj: { + /** + * 图片的路径,可以是相对路径,临时文件路径,存储文件路径,网络图片路径 + */ + src: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + saveImageToPhotosAlbum(obj: { + /** + * 图片文件路径,可以是临时文件路径也可以是永久文件路径,不支持网络图片路径 + */ + filePath: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 开始录音。当主动调用wx.stopRecord,或者录音超过1分钟时自动结束录音,返回录音文件的临时文件路径。当用户离开小程序时,此接口无法调用。 + */ + startRecord(obj: { + /** + * 录音成功后调用,返回录音文件的临时文件路径,res = {tempFilePath: '录音文件的临时路径'} + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * ​ 主动调用停止录音。 + */ + stopRecord(): void; + + /** + * 开始播放语音,同时只允许一个语音文件正在播放,如果前一个语音文件还没播放完,将中断前一个语音播放。 + */ + playVoice(obj: { + /** + * 需要播放的语音文件的文件路径 + */ + filePath: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 暂停正在播放的语音。再次调用wx.playVoice播放同一个文件时,会从暂停处开始播放。如果想从头开始播放,需要先调用 wx.stopVoice。 + */ + pauseVoice(): void; + + /** + * 结束播放语音。 + */ + stopVoice(): void; + + /** + * 获取后台音乐播放状态。 + */ + getBackgroundAudioPlayerState(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 使用后台播放器播放音乐,对于微信客户端来说,只能同时有一个后台音乐在播放。当用户离开小程序后,音乐将暂停播放;当用户点击“显示在聊天顶部”时,音乐不会暂停播放;当用户在其他小程序占用了音乐播放器,原有小程序内的音乐将停止播放。 + */ + playBackgroundAudio(obj: { + /** + * 音乐链接,目前支持的格式有 m4a, aac, mp3, wav + */ + dataUrl: string; + /** + * 音乐标题 + */ + title?: string; + /** + * 封面URL + */ + coverImgUrl?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 暂停播放音乐。 + */ + pauseBackgroundAudio(): void; + + /** + * 控制音乐播放进度。 + */ + seekBackgroundAudio(obj: { + /** + * 音乐位置,单位:秒 + */ + position: number; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 停止播放音乐。 + */ + stopBackgroundAudio(): void; + + /** + * 监听音乐播放。 + */ + onBackgroundAudioPlay(callback: Function): void; + + /** + * 监听音乐暂停。 + */ + onBackgroundAudioPause(callback: Function): void; + + /** + * 监听音乐停止。 + */ + onBackgroundAudioStop(callback: Function): void; + + getBackgroundAudioManager(): void; + + /** + * 创建并返回 audio 上下文 audioContext 对象 + */ + createAudioContext(audioId: string): IAudioContext; + + /** + * 拍摄视频或从手机相册中选视频,返回视频的临时文件路径。 + */ + chooseVideo(obj: { + /** + * album 从相册选视频,camera 使用相机拍摄,默认为:['album', 'camera'] + */ + sourceType?: string[]; + /** + * 拍摄视频最长拍摄时间,单位秒。最长支持 60 秒 + */ + maxDuration?: number; + /** + * 默认调起的为前置还是后置摄像头。front: 前置,back: 后置,默认 back + */ + camera?: string; + /** + * 接口调用成功,返回视频文件的临时文件路径,详见返回参数说明 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + saveVideoToPhotosAlbum(obj: { + /** + * 视频文件路径,可以是临时文件路径也可以是永久文件路径 + */ + filePath: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 创建并返回 video 上下文 videoContext 对象 + */ + createVideoContext(videoId: string): IVideoContext; + + // # 文件 # + + /** + * 保存文件到本地。 + */ + saveFile(obj: { + /** + * 需要保存的文件的临时路径 + */ + tempFilePath: string; + /** + * 返回文件的保存路径,res = {savedFilePath: '文件的保存路径'} + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 获取本地已保存的文件列表 + */ + getSavedFileList(obj: { + /** + * 接口调用成功的回调函数,返回结果见success返回参数说明 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 获取本地文件的文件信息。此接口只能用于获取已保存到本地的文件,若需要获取临时文件信息,请使用 wx.getFileInfo 接口。 + */ + getSavedFileInfo(obj: { + /** + * 文件路径 + */ + filePath: string; + /** + * 接口调用成功的回调函数,返回结果见success返回参数说明 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 删除本地存储的文件 + */ + removeSavedFile(obj: { + /** + * 需要删除的文件路径 + */ + filePath: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 新开页面打开文档,支持格式:doc, xls, ppt, pdf, docx, xlsx, pptx + */ + openDocument(obj: { + /** + * 文件路径,可通过 downFile 获得 + */ + filePath: string; + /** + * 文件类型,指定文件类型打开文件,有效值 doc, xls, ppt, pdf, docx, xlsx, pptx + */ + fileType?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getFileInfo(obj: { + /** + * 本地文件路径 + */ + filePath: string; + /** + * 计算文件摘要的算法,默认值 md5,有效值:md5,sha1 + */ + digestAlgorithm?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + // # 数据缓存 # + + /** + * 将数据存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个异步接口。 + */ + setStorage(obj: { + /** + * 本地缓存中的指定的 key + */ + key: string; + /** + * 需要存储的内容 + */ + data: any; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。 + */ + setStorageSync(key: string, data: any, ): void; + + /** + * 从本地缓存中异步获取指定 key 对应的内容。 + */ + getStorage(obj: { + /** + * 本地缓存中的指定的 key + */ + key: string; + /** + * 接口调用的回调函数,res = {data: key对应的内容} + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 从本地缓存中同步获取指定 key 对应的内容。 + */ + getStorageSync(key: string): void; + + /** + * 异步获取当前storage的相关信息 + */ + getStorageInfo(obj: { + /** + * 接口调用的回调函数,详见返回参数说明 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 同步获取当前storage的相关信息 + */ + getStorageInfoSync(): void; + + /** + * 从本地缓存中异步移除指定 key 。 + */ + removeStorage(obj: { + /** + * 本地缓存中的指定的 key + */ + key: string; + /** + * 接口调用的回调函数 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 从本地缓存中同步移除指定 key 。 + */ + removeStorageSync(key: string): void; + + /** + * 清理本地数据缓存。 + */ + clearStorage(): void; + + /** + * 同步清理本地数据缓存 + */ + clearStorageSync(): void; + + // # 位置 # + + /** + * 获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用;当用户点击“显示在聊天顶部”时,此接口可继续调用。 + */ + getLocation(obj: { + /** + * 默认为 wgs84 返回 gps 坐标,gcj02 返回可用于wx.openLocation的坐标 + */ + type?: string; + /** + * 接口调用成功的回调函数,返回内容详见返回参数说明。 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 打开地图选择位置 + */ + chooseLocation(obj: { + /** + * 接口调用成功的回调函数,返回内容详见返回参数说明。 + */ + success: Function; + /** + * 用户取消时调用 + */ + cancel?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * ​ 使用微信内置地图查看位置 + */ + openLocation(obj: { + /** + * 纬度,范围为-90~90,负数表示南纬 + */ + latitude: number; + /** + * 经度,范围为-180~180,负数表示西经 + */ + longitude: number; + /** + * 缩放比例,范围5~18,默认为18 + */ + scale?: number; + /** + * 位置名 + */ + name?: string; + /** + * 地址的详细说明 + */ + address?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 创建并返回 map 上下文 mapContext 对象 + */ + createMapContext(mapId: string): IMapContext; + + // # 设备 # + + /** + * 获取系统信息。 + */ + getSystemInfo(obj: { + /** + * 接口调用成功的回调 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 获取系统信息同步接口 + */ + getSystemInfoSync(): void; + + /** + * 获取网络类型。 + */ + getNetworkType(obj: { + /** + * 接口调用成功,返回网络类型 networkType + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + onNetworkStatusChange(callback: Function): void; + + setScreenBrightness(obj: { + /** + * 屏幕亮度值,范围 0~1,0 最暗,1 最亮 + */ + value: number; + /** + * 接口调用成功 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getScreenBrightness(obj: { + /** + * 接口调用成功 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + vibrateLong(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + vibrateShort(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 监听加速度数据,频率:5次/秒,接口调用后会自动开始监听,可使用 wx.stopAccelerometer 停止监听。 + */ + onAccelerometerChange(callback: Function): void; + + startAccelerometer(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + stopAccelerometer(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 监听罗盘数据,频率:5次/秒,接口调用后会自动开始监听,可使用wx.stopCompass停止监听。 + */ + onCompassChange(callback: Function): void; + + startCompass(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + stopCompass(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + makePhoneCall(obj: { + /** + * 需要拨打的电话号码 + */ + phoneNumber: string; + /** + * 接口调用成功的回调 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 调起客户端扫码界面,扫码成功后返回对应的结果 + */ + scanCode(obj: { + /** + * 是否只能从相机扫码,不允许从相册选择图片 + */ + onlyFromCamera?: boolean; + /** + * 接口调用成功的回调函数,返回内容详见返回参数说明。 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + setClipboardData(obj: { + /** + * 需要设置的内容 + */ + data: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getClipboardData(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + openBluetoothAdapter(obj: { + /** + * 成功则返回成功初始化信息 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + closeBluetoothAdapter(obj: { + /** + * 成功则返回成功关闭模块信息 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getBluetoothAdapterState(obj: { + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + onBluetoothAdapterStateChange(callback: Function): void; + + startBluetoothDevicesDiscovery(obj: { + /** + * 蓝牙设备主 service 的 uuid 列表 + */ + services?: Array; + /** + * 是否允许重复上报同一设备, 如果允许重复上报,则onDeviceFound 方法会多次上报同一设备,但是 RSSI 值会有不同 + */ + allowDuplicatesKey?: boolean; + /** + * 上报设备的间隔,默认为0,意思是找到新设备立即上报,否则根据传入的间隔上报 + */ + interval?: number; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + stopBluetoothDevicesDiscovery(obj: { + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getBluetoothDevices(obj: { + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + onBluetoothDeviceFound(callback: Function): void; + + getConnectedBluetoothDevices(obj: { + /** + * 蓝牙设备主 service 的 uuid 列表 + */ + services: Array; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + createBLEConnection(obj: { + /** + * 蓝牙设备 id,参考 getDevices 接口 + */ + deviceId: string; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + closeBLEConnection(obj: { + /** + * 蓝牙设备 id,参考 getDevices 接口 + */ + deviceId: string; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getBLEDeviceServices(obj: { + /** + * 蓝牙设备 id,参考 getDevices 接口 + */ + deviceId: string; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getBLEDeviceCharacteristics(obj: { + /** + * 蓝牙设备 id,参考 device 对象 + */ + deviceId: string; + /** + * 蓝牙服务 uuid + */ + serviceId: string; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + readBLECharacteristicValue(obj: { + /** + * 蓝牙设备 id,参考 device 对象 + */ + deviceId: string; + /** + * 蓝牙特征值对应服务的 uuid + */ + serviceId: string; + /** + * 蓝牙特征值的 uuid + */ + characteristicId: string; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + writeBLECharacteristicValue(obj: { + /** + * 蓝牙设备 id,参考 device 对象 + */ + deviceId: string; + /** + * 蓝牙特征值对应服务的 uuid + */ + serviceId: string; + /** + * 蓝牙特征值的 uuid + */ + characteristicId: string; + /** + * 蓝牙设备特征值对应的二进制值(注意:vConsole 无法打印出 ArrayBuffer 类型数据) + */ + value: undefined; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + notifyBLECharacteristicValueChange(obj: { + /** + * 蓝牙设备 id,参考 device 对象 + */ + deviceId: string; + /** + * 蓝牙特征值对应服务的 uuid + */ + serviceId: string; + /** + * 蓝牙特征值的 uuid + */ + characteristicId: string; + /** + * true: 启用 notify; false: 停用 notify + */ + state: boolean; + /** + * 成功则返回本机蓝牙适配器状态 + */ + success: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + onBLEConnectionStateChange(callback: Function): void; + + onBLECharacteristicValueChange(callback: Function): void; + + startBeaconDiscovery(obj: { + /** + * iBeacon设备广播的 uuids + */ + uuids: string[]; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + stopBeaconDiscovery(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getBeacons(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + onBeaconUpdate(callback: Function): void; + + onBeaconServiceChange(callback: Function): void; + + onUserCaptureScreen(callback: Function): void; + + addPhoneContact(obj: { + /** + * 头像本地文件路径 + */ + photoFilePath?: string; + /** + * 昵称 + */ + nickName?: string; + /** + * 姓氏 + */ + lastName?: string; + /** + * 中间名 + */ + middleName?: string; + /** + * 名字 + */ + firstName: string; + /** + * 备注 + */ + remark?: string; + /** + * 手机号 + */ + mobilePhoneNumber?: string; + /** + * 微信号 + */ + weChatNumber?: string; + /** + * 联系地址国家 + */ + addressCountry?: string; + /** + * 联系地址省份 + */ + addressState?: string; + /** + * 联系地址城市 + */ + addressCity?: string; + /** + * 联系地址街道 + */ + addressStreet?: string; + /** + * 联系地址邮政编码 + */ + addressPostalCode?: string; + /** + * 公司 + */ + organization?: string; + /** + * 职位 + */ + title?: string; + /** + * 工作传真 + */ + workFaxNumber?: string; + /** + * 工作电话 + */ + workPhoneNumber?: string; + /** + * 公司电话 + */ + hostNumber?: string; + /** + * 电子邮件 + */ + email?: string; + /** + * 网站 + */ + url?: string; + /** + * 工作地址国家 + */ + workAddressCountry?: string; + /** + * 工作地址省份 + */ + workAddressState?: string; + /** + * 工作地址城市 + */ + workAddressCity?: string; + /** + * 工作地址街道 + */ + workAddressStreet?: string; + /** + * 工作地址邮政编码 + */ + workAddressPostalCode?: string; + /** + * 住宅传真 + */ + homeFaxNumber?: string; + /** + * 住宅电话 + */ + homePhoneNumber?: string; + /** + * 住宅地址国家 + */ + homeAddressCountry?: string; + /** + * 住宅地址省份 + */ + homeAddressState?: string; + /** + * 住宅地址城市 + */ + homeAddressCity?: string; + /** + * 住宅地址街道 + */ + homeAddressStreet?: string; + /** + * 住宅地址邮政编码 + */ + homeAddressPostalCode?: string; + /** + * 接口调用成功 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + // # 界面 # + + /** + * 显示消息提示框 + */ + showToast(obj: { + /** + * 提示的内容 + */ + title: string; + /** + * 图标,有效值 "success", "loading" + */ + icon?: string; + /** + * 自定义图标的本地路径,image 的优先级高于 icon + */ + image?: string; + /** + * 提示的延迟时间,单位毫秒,默认:1500 + */ + duration?: number; + /** + * 是否显示透明蒙层,防止触摸穿透,默认:false + */ + mask?: boolean; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + showLoading(obj: { + /** + * 提示的内容 + */ + title: string; + /** + * 是否显示透明蒙层,防止触摸穿透,默认:false + */ + mask?: boolean; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 隐藏消息提示框 + */ + hideToast(): void; + + hideLoading(): void; + + /** + * ​显示模态弹窗 + */ + showModal(obj: { + /** + * 提示的标题 + */ + title: string; + /** + * 提示的内容 + */ + content: string; + /** + * 是否显示取消按钮,默认为 true + */ + showCancel?: boolean; + /** + * 取消按钮的文字,默认为"取消",最多 4 个字符 + */ + cancelText?: string; + /** + * 取消按钮的文字颜色,默认为"#000000" + */ + cancelColor?: undefined; + /** + * 确定按钮的文字,默认为"确定",最多 4 个字符 + */ + confirmText?: string; + /** + * 确定按钮的文字颜色,默认为"#3CC51F" + */ + confirmColor?: undefined; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * ​显示操作菜单 + */ + showActionSheet(obj: { + /** + * 按钮的文字数组,数组长度最大为6个 + */ + itemList: undefined; + /** + * 按钮的文字颜色,默认为"#000000" + */ + itemColor?: undefined; + /** + * 接口调用成功的回调函数,详见返回参数说明 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + setTopBarText(obj: { + /** + * 置顶栏文字内容 + */ + text: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 动态设置当前页面的标题。 + */ + setNavigationBarTitle(obj: { + /** + * 页面标题 + */ + title: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 在当前页面显示导航条加载动画。 + */ + showNavigationBarLoading(): void; + + /** + * 隐藏导航条加载动画。 + */ + hideNavigationBarLoading(): void; + + /** + * 保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面。 + */ + navigateTo(obj: { + /** + * 需要跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2' + */ + url: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 关闭当前页面,跳转到应用内的某个页面。 + */ + redirectTo(obj: { + /** + * 需要跳转的应用内非 tabBar 的页面的路径,路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2' + */ + url: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + reLaunch(obj: { + /** + * 需要跳转的应用内页面路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 'path?key=value&key2=value2',如果跳转的页面路径是 tabBar 页面则不能带参数 + */ + url: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 + */ + switchTab(obj: { + /** + * 需要跳转的 tabBar 页面的路径(需在 app.json 的 tabBar 字段定义的页面),路径后不能带参数 + */ + url: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages()) 获取当前的页面栈,决定需要返回几层。 + */ + navigateBack(obj: { + /** + * 返回的页面数,如果 delta 大于现有页面数,则返回到首页。 + */ + delta?: number; + }): void; + + /** + * 创建一个动画实例animation。调用实例的方法来描述动画。最后通过动画实例的export方法导出动画数据传递给组件的animation属性。 + */ + createAnimation(obj: { + /** + * 400 + */ + duration?: number; + /** + * "linear" + */ + timingFunction?: string; + /** + * 0 + */ + delay?: number; + /** + * "50% 50% 0" + */ + transformOrigin?: string; + }): IAnimation; + + pageScrollTo(obj: { + /** + * 滚动到页面的目标位置(单位px) + */ + scrollTop: number; + }): void; + + /** + * 创建 canvas 绘图上下文(指定 canvasId).Tip: 需要指定 canvasId,该绘图上下文只作用于对应的 + */ + createCanvasContext(canvasId: string): ICanvasContext; + + /** + * 把当前画布的内容导出生成图片,并返回文件路径 + */ + canvasToTempFilePath(canvasId: string): void; + + startPullDownRefresh(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 停止当前页面下拉刷新。 + */ + stopPullDownRefresh(): void; + + // # WXML节点信息 # + + // # 第三方平台 # + + getExtConfig(obj: { + /** + * 返回第三方平台自定义的数据 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getExtConfigSync(): void; + + // # 开放接口 # + + /** + * 调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)。用户数据的加解密通讯需要依赖会话密钥完成。 + */ + login(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 通过上述接口获得的用户登录态拥有一定的时效性。用户越久未使用小程序,用户登录态越有可能失效。反之如果用户一直在使用小程序,则用户登录态一直保持有效。具体时效逻辑由微信维护,对开发者透明。开发者只需要调用wx.checkSession接口检测当前用户登录态是否有效。登录态过期后开发者可以再调用wx.login获取新的用户登录态。 + */ + checkSession(obj: { + /** + * 接口调用成功的回调函数,登录态未过期 + */ + success?: Function; + /** + * 接口调用失败的回调函数,登录态已过期 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + authorize(obj: { + /** + * 需要获取权限的scope,详见 scope 列表 + */ + scope: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 获取用户信息,withCredentials 为 true 时需要先调用 wx.login 接口。 + */ + getUserInfo(obj: { + /** + * 是否带上登录态信息 + */ + withCredentials?: boolean; + /** + * 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文 + */ + lang?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + /** + * 发起微信支付。 + */ + requestPayment(obj: { + /** + * 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间 + */ + timeStamp: string; + /** + * 随机字符串,长度为32个字符以下。 + */ + nonceStr: string; + /** + * 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=* + */ + package: string; + /** + * 签名算法,暂支持 MD5 + */ + signType: string; + /** + * 签名,具体签名方案参见小程序支付接口文档; + */ + paySign: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + chooseAddress(obj: { + /** + * 返回用户选择的收货地址信息 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + addCard(obj: { + /** + * 需要添加的卡券列表,列表内对象说明请参见请求对象说明 + */ + cardList: undefined; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + openCard(obj: { + /** + * 需要打开的卡券列表,列表内参数详见openCard 请求对象说明 + */ + cardList: undefined; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + openSetting(obj: { + /** + * 接口调用成功的回调函数,返回内容详见返回参数说明。 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getSetting(obj: { + /** + * 接口调用成功的回调函数,返回内容详见返回参数说明。 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + getWeRunData(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + navigateToMiniProgram(obj: { + /** + * 要打开的小程序 appId + */ + appId: string; + /** + * 打开的页面路径,如果为空则打开首页 + */ + path?: string; + /** + * 需要传递给目标小程序的数据,目标小程序可在 App.onLaunch(),App.onShow() 中获取到这份数据。详情 + */ + extraData?: any; + /** + * 要打开的小程序版本,有效值 develop(开发版),trial(体验版),release(正式版) ,仅在当前小程序为开发版或体验版时此参数有效;如果当前小程序是体验版或正式版,则打开的小程序必定是正式版。默认值 release + */ + envVersion?: string; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + chooseInvoiceTitle(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + checkIsSupportSoterAuthentication(obj: { + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + + // # 数据 # + + /** + * 自定义分析数据上报接口。使用前,需要在小程序管理后台自定义分析中新建事件,配置好事件名与字段。 + */ + reportAnalytics(eventName: string, data: string, ): void; + + // # 拓展接口 # + + arrayBufferToBase64(arrayBuffer: string): void; + + base64ToArrayBuffer(base64: string): void; + + // # 调试接口 # + + setEnableDebug(obj: { + /** + * 是否打开调试 + */ + enableDebug: boolean; + /** + * 接口调用成功的回调函数 + */ + success?: Function; + /** + * 接口调用失败的回调函数 + */ + fail?: Function; + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: Function; + }): void; + +} \ No newline at end of file diff --git a/utils/CommonUtil.js b/utils/CommonUtil.js new file mode 100644 index 0000000..79dc7c9 --- /dev/null +++ b/utils/CommonUtil.js @@ -0,0 +1,293 @@ +const CommonUtil = { + + /** + * 获取当前时间 + */ + getCurrenTime(){ + // 创建一个 Date 对象,表示当前时间 + let now = new Date(); + // 获取年、月、日、时、分、秒 + + // 获取年月日时分秒 + let time = { + year: now.getFullYear().toString().slice(-2), // 获取年份后两位 + month: (now.getMonth() + 1), // 月份从0开始,需加1 + day: now.getDate(), + hours: now.getHours(), + minutes: now.getMinutes(), + seconds: now.getSeconds() + }; + return time; + }, + + /** + * 通过时间戳 获取年月日 时分秒 + * @param {10位 时间戳} timestamp + */ + getDateByTimestamp(timestamp){ + + if(timestamp === 0){ + return { + year:0, + month:0, + day:0, + hours:0, + minutes:0, + seconds:0 + } + } + + // 将时间戳转为毫秒(JavaScript的Date对象使用毫秒为单位) + const date = new Date(timestamp * 1000); + + // 格式化为年月日时分秒 + const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`; + console.log(formattedDate); // 输出:2024-12-20 00:00:00 + return { + year:date.getFullYear() - 2000, + month:date.getMonth() + 1, + day:date.getDate(), + hours:date.getHours(), + minutes:date.getMinutes(), + seconds:date.getSeconds() + } + }, + + + /** + * 发送屏幕亮度,0~5级别转换 + * @param {*} level + */ + sendSBLevelConv(level){ + switch(level){ + case 0: + return 0x8F; + case 1: + return 0x88; + case 2: + return 0x86; + case 3: + return 0x84; + case 4: + return 0x82; + case 5: + return 0x80; + default: + return 0x84;//如果不是0~5,则默认均衡亮度 + } + }, + + /** + * 获得屏幕亮度,0~5级别转换 + * @param {*} level + */ + respSBLevelConv(level){ + switch(level){ + case 0x0F: + return 0; + case 0x0D: + return 1; + case 0x0B: + return 2; + case 0x09: + return 3; + case 0x07: + return 4; + case 0x05: + return 5; + default: + return 2;//如果不是0~5,则默认均衡亮度 + } + }, + + getWeekBinary(week) { + let binaryValue = 0; + for (let i = 0; i < week.length; i++) { + // 逐位设置,week[0] 代表周天(offset 0) + binaryValue |= week[i] << i; + } + return binaryValue; + }, + + stringToBytes(str) { + let encoded = unescape(encodeURIComponent(str)); + let bytes = []; + for (let i = 0; i < encoded.length; i++) { + bytes.push(encoded.charCodeAt(i)); + } + console.log("bytes:",bytes) + return bytes; + }, + + bytesToString(bytes) { + let str = ''; + for (let i = 0; i < bytes.length; i++) { + str += String.fromCharCode(bytes[i]); + } + return decodeURIComponent(escape(str)); // 确保正确解码 + }, + + + /** + * 字段转指令 + * @param {字段} field + */ + fieldToCommand(field){ + switch(field){ + case "sleepLastTime": + return 0x53; + case "heartRateLastTime": + return 0x55; + case "temperatureLastTime": + return 0x65; + case "bloodOxygenLastTime": + return 0x66; + case "bloodPressureLastTime": + return 0x56; + case "stepLastTime": + return 0x52; + default: + return 0; + } + }, + + /** + * 指令转字段 + * @param {指令} command + */ + commandToField(command){ + switch(command){ + case 0x53: + return "sleepLastTime"; + case 0x55: + return "heartRateLastTime"; + case 0x65: + return "temperatureLastTime"; + case 0x66: + return "bloodOxygenLastTime"; + case 0x56: + return "bloodPressureLastTime"; + case 0x52: + return "stepLastTime"; + default: + return 0; + } + }, + + + /** + * 组装睡眠数据 + */ + assembleSleepData(sleepArray) { + let dateObj = sleepArray[0]; // 获取第一个数据作为比对的时间 + let res = []; // 存放最终结果 + let resItem = { // 每天的睡眠数据对象 + date: '', + sleepList: [] + }; + + // 取出第一个数据并初始化 + let firstSleepData = sleepArray[0]; + resItem.date = firstSleepData.yyyyMMdd; + resItem.sleepList.push(firstSleepData); // 将第一个数据放入到 `sleepList` + sleepArray.forEach(item => { + // 判断是否是同一天的数据 + let isSameDay = this.timeComparison(dateObj.yyyyMMdd, item.yyyyMMdd, item.minute); + + // 如果状态发生变化,且日期不同,就表示开始新的睡眠数据 + if (item.status !== firstSleepData.status) { + if (isSameDay) { + resItem.sleepList.push(item); // 如果是同一天,继续添加到当天的睡眠数据 + } else { + res.push(resItem); // 否则,将当天的数据推入结果数组 + resItem = { // 新的一天开始 + date: item.yyyyMMdd, + sleepList: [item] // 将当前项作为新的睡眠数据开始 + }; + dateObj = item; // 更新 `dateObj`,用于比对后续数据 + } + } + firstSleepData = item; // 更新当前比对的睡眠数据 + }); + + // 最后一次的睡眠数据也需要被加入到 `res` + if (resItem.sleepList.length > 0) { + res.push(resItem); + } + + return res; // 返回按天拆分后的睡眠数据 + }, + + + /** + * 计算出一天的时间范围 + * @param {日期} dateStr + */ + getPreviousDayTimeRange(dateStr) { + // 将 "YYYYMMDD" 格式的日期转换为 "YYYY-MM-DD" 格式,方便解析 + const formattedDateStr = dateStr.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'); + + // 创建目标日期对象 + const date = new Date(formattedDateStr); // 例如:2024-12-12 + + // 1. 获取前一天的 20:00:00 + const prevDayStart = new Date(date); // 克隆原日期 + prevDayStart.setDate(date.getDate() - 1); // 设置为前一天 + prevDayStart.setHours(20, 0, 0, 0); // 设置为20:00:00 + + // 2. 获取当天的 10:00:00 + const nextDayEnd = new Date(date); // 克隆原日期 + nextDayEnd.setDate(date.getDate()); // 保持为当天 + nextDayEnd.setHours(10, 0, 0, 0); // 设置为10:00:00 + + // 返回时间戳 + return { + startTimestamp: prevDayStart.getTime(), // 转换为10位时间戳 + endTimestamp: nextDayEnd.getTime() // 转换为10位时间戳 + }; + }, + + + /** + * + * @param {日期} dateStr + * @param {一天的第N分钟} minutes + */ + getTimestampFromDateAndMinutes(dateStr, minutes) { + // 将 "YYYYMMDD" 格式的日期转换为 "YYYY-MM-DD" 格式,方便解析 + const formattedDateStr = dateStr.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'); + // 创建目标日期对象 + const date = new Date(formattedDateStr); // 例如:2024-12-12 + // 计算总分钟数 + const totalMinutes = minutes; + // 将分钟数转化为小时和分钟 + const hours = Math.floor(totalMinutes / 60); // 计算小时数 + const mins = totalMinutes % 60; // 计算剩余的分钟数 + // 设置时间为指定的小时和分钟 + date.setHours(hours, mins, 0, 0); // 设置为相应的时分秒 + // 返回时间戳,转换为10位时间戳 + return date.getTime(); + }, + + + /** + * + * @param {日期范围} date + * @param {分钟} minute + * @param {比较值} comparisonValue + */ + timeComparison(date,comparisonValue,minute){ + let dateRange = this.getPreviousDayTimeRange(date); + let time = this.getTimestampFromDateAndMinutes(comparisonValue,minute); + if(time >= dateRange.startTimestamp && time <= dateRange.endTimestamp){ + // 在范围内,是同一天睡眠 + return true; + } + return false; + } + + +} + +// 导出模块 +module.exports = CommonUtil; \ No newline at end of file diff --git a/utils/DataPacket.js b/utils/DataPacket.js new file mode 100644 index 0000000..06a2772 --- /dev/null +++ b/utils/DataPacket.js @@ -0,0 +1,455 @@ +// 数据包组装模块 +const { calculateCRC } = require('./crc'); +const CommonUtil = require('./CommonUtil'); +const WeatherUtils = require('./WeatherUtils'); +const HealthDataCache = require('./HealthDataCache.js'); + + +const DataPacket = { + // 转换为 BCD 格式 + toBCD(value) { + return ((value / 10) << 4) | (value % 10); + }, + + // 组装时间设置命令包 + generateSetTimePacket() { + + let arr = CommonUtil.getCurrenTime(); + // 将时间参数转换为BCD格式 + const bcdYear = this.toBCD(arr.year); + const bcdMonth = this.toBCD(arr.month); + const bcdDay = this.toBCD(arr.day); + const bcdHour = this.toBCD(arr.hours); + const bcdMinute = this.toBCD(arr.minutes); + const bcdSecond = this.toBCD(arr.seconds); + + // 创建Uint8Array + const packet = new Uint8Array(16); + + // 填充命令数据包 + packet[0] = 0x01; // 命令字节 + packet[1] = bcdYear; + packet[2] = bcdMonth; + packet[3] = bcdDay; + packet[4] = bcdHour; + packet[5] = bcdMinute; + packet[6] = bcdSecond; + + // 填充其他字节为 0 + packet.fill(0x00, 7, 15); + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + }, + + generateSetUserInfoPacket(gender, age, height, weight) { + // 输入参数验证 + if (![0, 1].includes(gender)) { + throw new Error('性别只能为 0 或 1'); + } + if (age < 0 || age > 150) { + throw new Error('年龄无效'); + } + if (height < 0 || height > 300) { + throw new Error('身高无效'); + } + if (weight < 0 || weight > 500) { + throw new Error('体重无效'); + } + + // 创建 Uint8Array 数据包 + const packet = new Uint8Array(16); + + // 填充命令数据包 + packet[0] = 0x02; // 命令字节 + packet[1] = gender; // 性别 + packet[2] = age; // 年龄 + packet[3] = height; // 身高 + packet[4] = weight; // 体重 + packet[5] = 70; // 步长 + + // 填充其他字节为 0 + packet.fill(0x00, 6, 15); + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + }, + + /** + * 设置手环基本参数 + * @param {抬腕亮屏 1-开启,0-关闭} WRSFlag + * @param {屏幕亮度级别 分6个等级,参数传值:0-5,0:最暗,5:最亮。} SBLevel + */ + generateWatchBasicPacket(WRSFlag,SBLevel){ + const packet = new Uint8Array(16); + + // 填充命令数据包 + packet[0] = 0x03; // 命令字节 + packet[1] = 0x80; // 固定值,距离单位:0x81:MILE,0x80:KM + packet[2] = 0x80; // 固定值,12小时24小时显示:0x81: 12小时制, 0x80:24小时制度 + packet[3] = WRSFlag==1 ? 0x81:0x80; // 固定值,抬腕亮屏 0x81:开启, 0x80:关闭 + packet[4] = 0x80; // 固定值,温度单位切换 0x81:华摄氏度, 0x80:摄氏度,默认摄氏度 + packet[5] = 0x80; // 固定值,夜间模式 0x81:开启, 0x80:关闭(夜间模式开启后晚上六点到凌晨7点,自动调节显示亮度到最暗) + packet[6] = 0x80; // 固定值,ANCS使能开关 0x81:开启, 0x80:关闭 + packet.fill(0x00, 7, 10); // 填充其他字节为0 + packet[11] = CommonUtil.sendSBLevelConv(SBLevel);// 屏幕亮度 + packet[12] = 0x80;// 表盘界面更换80-8A + packet[13] = 0x80;// 社交距离开关 0x81 开 0x80 关 + packet[14] = 0x81;//中英文切换,80代表英文(默认),81代表中文 + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + console.log("Generated Packet:", packet); // 调试最终数据包 + return packet; + }, + + /** + * 设置手环基本参数 + * @param {抬腕亮屏 1-开启,0-关闭} WRSFlag + */ + getWatchBasic(){ + const packet = new Uint8Array(16); + packet[0] = 0x04; + packet[1] = 0x00; + packet.fill(0x00, 2, 15); + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + return packet; + }, + + // 获取电量 + generateBatteryCheckPacket() { + // 创建 Uint8Array 数据包 + const packet = new Uint8Array(16); + + // 填充命令数据包 + packet[0] = 0x13; // 命令字节 + packet[1] = 0x00; // 固定值,表示电量检测 + packet.fill(0x00, 2, 15); // 填充其他字节为0 + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + }, + + /** + * + * @param {指令} command + */ + generateReadDataPacket(command){ + // 创建 Uint8Array 数据包 + const packet = new Uint8Array(16); + + packet[0] = command; // 命令字节 + packet[1] = 0x00; //0x99: 删除数据,0x00:读取最近数据,0x01:读取指定位置数据,0x02:继续读取上一次位置 + let field = CommonUtil.commandToField(command);// 根据指令获取字段 + // console.log("field>>>:",field); + let lastTime = HealthDataCache.getLastTimeField(field);// 根据字段获取最近一次同步数据的时间戳 + // console.log("lastTime1>>>:",lastTime); + + let date = CommonUtil.getDateByTimestamp(lastTime); + packet[2] = 0x00; // 占位 + packet[3] = 0x00; // 占位 + + packet[4] = "0x"+ date.year; //年 + packet[5] = "0x"+ date.month; //月 + packet[6] = "0x"+ date.day; //日 + packet[7] = "0x"+ date.hours; //时 + packet[8] = "0x"+ date.minutes; //分 + packet[9] = "0x"+ date.seconds; //秒 + + packet.fill(0x00, 10, 15); // 填充其他字节为0 + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + + + }, + + // 获取步数总数 + generateReadStepTotalDataPacket() { + // 创建 Uint8Array 数据包 + const packet = new Uint8Array(16); + + // 填充命令数据包 + packet[0] = 0x51; // 命令字节 + packet[1] = 0x00; // 读取记步总数据 + packet.fill(0x00, 2, 15); // 填充其他字节为0 + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + }, + + // 获取睡眠数据 + generateReadSleepDataPacket() { + const packet = new Uint8Array(16); + // 填充命令数据包 + packet[0] = 0x53; + packet[1] = 0x00; + packet.fill(0x00, 2, 15); + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + }, + + // 生成获取时间命令包 + generateGetTimePacket() { + const packet = new Uint8Array(16); + + // 填充数据 + packet[0] = 0x41; // 命令字节 + packet.fill(0x00, 1, 15); // 填充其他字节为 0 + + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + + return packet; + }, + + // 生成闹钟、吃药、喝水提示 + generateAlarmClockPackets(maxLength, alarms) { + const commands = new Uint8Array(maxLength); // 创建固定长度的 Uint8Array + let offset = 0; // 当前写入位置 + const packetLength = 39; // 单个指令长度 + const maxAlarmsPerPacket = Math.floor(maxLength / packetLength); // 每条指令最大包含闹钟个数 + const totalPackets = Math.ceil(alarms.length / maxAlarmsPerPacket); // 总共需要多少条指令 + + for (let i = 0; i < totalPackets; i++) { + const startIdx = i * maxAlarmsPerPacket; + const endIdx = Math.min(startIdx + maxAlarmsPerPacket, alarms.length); + + for (let j = startIdx; j < endIdx; j++) { + const alarm = alarms[j]; + + if (offset + packetLength > maxLength) { + throw new Error("Maximum length exceeded, cannot fit all alarms."); + } + + // 写入闹钟数据 + commands[offset++] = 0x23; // 固定头部 + commands[offset++] = alarms.length; // XX: 总闹钟个数 + commands[offset++] = alarm.id; // AA: 闹钟编号 + commands[offset++] = alarm.enabled ? 0x01 : 0x00; // BB: Enable/Disable + commands[offset++] = alarm.type; // CC: 闹钟类型 1:闹钟,2:吃药提示,3:喝水提示。 + commands[offset++] = this.toBCD(alarm.hour); // DD: 小时 (BCD) + commands[offset++] = this.toBCD(alarm.minute); // EE: 分钟 (BCD) + commands[offset++] = alarm.weekEnable; // FF: 星期使能 + commands[offset++] = alarm.text.length; // GG: 文本长度 + + // 写入文本内容或填充 0x00 + for (let t = 0; t < 15; t++) { + // commands[offset++] = t < alarm.text.length ? alarm.text.charCodeAt(t) : 0x00; + if (t < alarm.text.length) { + const charCode = alarm.text.charCodeAt(t); + commands[offset++] = (charCode >> 8) & 0xFF; // 高字节 + commands[offset++] = charCode & 0xFF; // 低字节 + } else { + commands[offset++] = 0x00; // 填充 0x00 + commands[offset++] = 0x00; + } + } + } + } + + // 尾巴 + commands[offset++] = 0x23; + commands[offset++] = 0xFF; + return commands; + }, + + /** + * 获取闹钟数据 + */ + getAlarmClock(){ + const packet = new Uint8Array(16); + packet[0] = 0x57; + packet[1] = 0x00; + packet.fill(0x00, 2, 15); + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + return packet; + }, + + /** + * + * @param {开始 时} startHour + * @param {开始 分} startMinute + * @param {结束 时} endHour + * @param {结束 分} endMinute + * @param {星期天 至 星期六周} weekDays + * @param {运动提醒周期,单位为分钟} reminderInterval + * @param {最少运动步数} minSteps + * @param {运动总开关} sportSwitch + */ + generateSedentaryPacket(startHour, startMinute, endHour, endMinute, weekDays, reminderInterval, minSteps, sportSwitch){ + const packet = new Uint8Array(16); + // 填充命令数据包 + packet[0] = 0x25; // 命令字节 + + // 开始运动时间 + packet[1] = startHour; // AA: 开始运动时间的小时部分 + packet[2] = startMinute; // BB: 开始运动时间的分钟部分 + + // 结束运动时间 + packet[3] = endHour; // CC: 结束运动时间的小时部分 + packet[4] = endMinute; // DD: 结束运动时间的分钟部分 + + let binaryWeek = CommonUtil.getWeekBinary(weekDays); + packet[5] = binaryWeek; // EE: 星期日到星期六 + // 运动提醒周期(FF) + packet[6] = reminderInterval; // FF: 运动提醒周期,单位为分钟 + // 最少运动步数(GG) + packet[7] = minSteps; // GG: 最少运动步数 + // 运动总开关(HH) + packet[8] = sportSwitch === 1 ? 0x01 : 0x00; // HH: 运动总开关 1-开启 0-关闭 + // 填充后续字节为0x00 + packet.fill(0x00, 9, 15); + // 计算CRC并设置CRC字节(CRC算法需根据协议提供) + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; // CRC校验字节 + return packet; + }, + + /** + * 获取久坐数据 + */ + getSedentary(){ + const packet = new Uint8Array(16); + packet[0] = 0x26; + packet[1] = 0x00; + packet.fill(0x00, 2, 15); + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + return packet; + }, + + /** + * 设置运动目标 + * @param {步数目标} targetSteps + * @param {目标时间} targetTime + * @param {距离目标} targetDistance + * @param {卡路里目标} targetCalories + * @param {睡眠目标} targetSleep + */ + generateStepTargetPacket(targetSteps, targetTime, targetDistance, targetCalories, targetSleep){ + // 1. 组装目标步数(4字节,低字节在前) + const stepBytes = [ + targetSteps & 0xFF, // AA: 低字节 + (targetSteps >> 8) & 0xFF, // BB: 次低字节 + (targetSteps >> 16) & 0xFF, // CC: 次高字节 + (targetSteps >> 24) & 0xFF // DD: 高字节 + ]; + // 2. 组装目标步数时间(2字节,分钟,低字节在前) + const timeBytes = [ + targetTime & 0xFF, // EE: 低字节 + (targetTime >> 8) & 0xFF // FF: 高字节 + ]; + // 3. 组装目标距离(2字节,低字节在前) + const distanceBytes = [ + targetDistance & 0xFF, // GG: 低字节 + (targetDistance >> 8) & 0xFF // HH: 高字节 + ]; + // 4. 组装目标卡路里(2字节,低字节在前) + const caloriesBytes = [ + targetCalories & 0xFF, // II: 低字节 + (targetCalories >> 8) & 0xFF // JJ: 高字节 + ]; + // 5. 组装目标睡眠时间(2字节,分钟,低字节在前) + const sleepBytes = [ + targetSleep & 0xFF, // KK: 低字节 + (targetSleep >> 8) & 0xFF // LL: 高字节 + ]; + // 6. 组装命令字节(0x0B) + const commandByte = 0x0B; + // 7. 保留字节 + const reservedBytes = [0x00, 0x00]; + // 8. 拼接所有字节 + const packet = [ + commandByte, ...stepBytes, ...timeBytes, ...distanceBytes, + ...caloriesBytes, ...sleepBytes, ...reservedBytes + ]; + // 9. 计算CRC校验字节 + const crc = calculateCRC(packet); // 需要实现CRC校验算法 + // 10. 将CRC字节添加到数据包末尾 + packet.push(crc); + return new Uint8Array(packet); + }, + + /** + * 获取运动目标 + */ + getStepTarget(){ + const packet = new Uint8Array(16); + packet[0] = 0x4B; + packet[1] = 0x00; + packet.fill(0x00, 2, 15); + // 计算并设置 CRC 校验字节 + const crc = calculateCRC(packet.subarray(0, 15)); + packet[15] = crc; + return packet; + }, + + /** + * + * @param {当前天气} weather + * @param {当前气温} temperature + * @param {最大气温} maxTemperature + * @param {最小气温} minTemperature + * @param {空气质量} aqi + * @param {地址长度} length + * @param {地址,最大32位} address + */ + generateWeatherPacket(weather,temperature,maxTemperature,minTemperature,aqi,address){ + let aqiInt = parseInt(aqi); + const packet = new Uint8Array(8); + packet[0] = 0x15; // 命令字节 + const weatherCode = WeatherUtils.getWeatherCode(weather); + packet[1] = weatherCode; + packet[2] = parseInt(temperature); + packet[4] = parseInt(maxTemperature); + packet[3] = parseInt(minTemperature); + packet[5] = aqiInt & 0xFF;// 低字节 + packet[6] = (aqiInt >> 8) & 0xFF; //高字节 + // 将地址转换为ASCII字节数组 + const addressBytes = CommonUtil.stringToBytes(address); + packet[7] = parseInt(addressBytes.length); + + const text = new Uint8Array(addressBytes.length); + // 填充地址到数据包中 + for (let i = 0; i < addressBytes.length; i++) { + text[i] = addressBytes[i]; + } + // 拼接 header 和 payload + const fullPackage = new Uint8Array(packet.length + addressBytes.length); + fullPackage.set(packet, 0); + fullPackage.set(text, 8); + return fullPackage; + }, + +} + + +// 导出模块 +module.exports = DataPacket; diff --git a/utils/DynamicArrayBuffer.js b/utils/DynamicArrayBuffer.js new file mode 100644 index 0000000..47ec7d7 --- /dev/null +++ b/utils/DynamicArrayBuffer.js @@ -0,0 +1,43 @@ +// utils/dynamicArrayBuffer.js +class DynamicArrayBuffer { + constructor(initialSize = 1024) { + this.buffer = new ArrayBuffer(initialSize); // 初始的ArrayBuffer + this.view = new Uint8Array(this.buffer); // Uint8Array视图,用于操作ArrayBuffer + this.offset = 0; // 记录当前已写入的字节数 + } + + // 扩展ArrayBuffer的大小 + expandBuffer(newSize) { + const newBuffer = new ArrayBuffer(newSize); // 创建新的ArrayBuffer + const newView = new Uint8Array(newBuffer); // 创建新的Uint8Array视图 + + // 复制旧数据到新buffer + newView.set(this.view); + + // 更新当前buffer和视图 + this.buffer = newBuffer; + this.view = newView; + } + + // 向ArrayBuffer添加数据 + appendData(data) { + const dataLength = data.length; + + // 如果当前的数据空间不足,扩展buffer + if (this.offset + dataLength > this.buffer.byteLength) { + let newSize = Math.max(this.buffer.byteLength * 2, this.offset + dataLength); + this.expandBuffer(newSize); // 扩展buffer + } + + // 将数据写入buffer + this.view.set(data, this.offset); + this.offset += dataLength; // 更新偏移量 + } + + // 获取当前缓冲区的内容 + getBuffer() { + return this.view.slice(0, this.offset); // 返回有效数据部分 + } +} + +module.exports = DynamicArrayBuffer; diff --git a/utils/HealthDataCache.js b/utils/HealthDataCache.js new file mode 100644 index 0000000..c8b7260 --- /dev/null +++ b/utils/HealthDataCache.js @@ -0,0 +1,116 @@ +const HealthDataCache = { + CACHE_KEY: 'healthDataCache', + + // 获取缓存数据,若没有缓存则返回初始数据 + getCache() { + let cache = wx.getStorageSync(this.CACHE_KEY); + if (!cache) { + // 初始化缓存数据 + cache = { + sleepLastTime: 0, + heartRateLastTime: 0, + temperatureLastTime: 0, + bloodOxygenLastTime: 0, + bloodPressureLastTime: 0, + stepLastTime:0 + }; + wx.setStorageSync(this.CACHE_KEY, cache); // 如果没有缓存,设置默认值 + } + return cache; + }, + + // 更新缓存数据 + updateCache(field, value) { + const cache = this.getCache(); // 获取缓存数据 + if (cache[field] !== undefined) { + cache[field] = value; // 更新对应字段 + wx.setStorageSync(this.CACHE_KEY, cache); // 保存更新后的缓存数据 + console.log(`缓存更新成功,${field} 设置为 ${value}`); + } else { + console.log(`字段 ${field} 不存在,无法更新`); + } + }, + + // 更新血压数据 + updateBloodPressureData(newDate) { + let lastTime = this.getLastTimeField('bloodPressureLastTime'); + if(newDate > lastTime){ + this.updateCache('bloodPressureLastTime', newDate); + } + }, + + // 更新心率数据 + updateHeartRateData(newDate) { + // 判断最新传入的时间戳是否 大于 上次更新数据的时间,只有大于才更新缓存时间 + let lastTime = this.getLastTimeField('heartRateLastTime'); + if(newDate > lastTime){ + console.log("本次更新时间大于上次更新时间"); + console.log("上次更新时间>>>:",lastTime); + console.log("本次更新时间>>>:",newDate); + this.updateCache('heartRateLastTime', newDate); + } + }, + + // 更新体温数据 + updateTemperatureData(newDate) { + let lastTime = this.getLastTimeField('temperatureLastTime'); + if(newDate > lastTime){ + this.updateCache('temperatureLastTime', newDate); + } + + }, + + // 更新血氧数据 + updateBloodOxygenData(newDate) { + let lastTime = this.getLastTimeField('bloodOxygenLastTime'); + if(newDate > lastTime){ + this.updateCache('bloodOxygenLastTime', newDate); + } + }, + + // 更新睡眠数据 + updateSleepData(newDate) { + let lastTime = this.getLastTimeField('sleepLastTime'); + if(newDate > lastTime){ + this.updateCache('sleepLastTime', newDate); + } + }, + + // 更新步数数据 + updateStepData(newDate) { + let lastTime = this.getLastTimeField('stepLastTime'); + if(newDate > lastTime){ + this.updateCache('stepLastTime', newDate); + } + }, + + // 更新所有时间字段的时间 + updateAllFieldsTime(newDate) { + this.updateCache('sleepLastTime', newDate); + this.updateCache('heartRateLastTime', newDate); + this.updateCache('temperatureLastTime', newDate); + this.updateCache('bloodOxygenLastTime', newDate); + this.updateCache('bloodPressureLastTime', newDate); + this.updateCache('stepLastTime', newDate); + console.log(`所有字段时间更新为: ${newDate}`); + }, + + // 获取缓存中指定字段的值 + getField(field) { + const cache = this.getCache(); + return cache[field]; + }, + + // 根据字段获取时间戳,若返回空,则默认取过去7天,作为起始时间 + getLastTimeField(field){ + let lastTime = this.getField(field); + if(lastTime === null || lastTime === ''|| lastTime === undefined){ + // 获取九宫格的数据时间作为 开始同步时间,这里暂时获取当前时间戳 + return new Date().getTime() / 1000; + } + return lastTime; + }, + +}; +// 导出模块 +module.exports = HealthDataCache; diff --git a/utils/OAT/DFUDef.js b/utils/OAT/DFUDef.js new file mode 100644 index 0000000..5040bc3 --- /dev/null +++ b/utils/OAT/DFUDef.js @@ -0,0 +1,114 @@ +// Constants +const AN_DEVICE_DFU_DATA = "00006387-3C17-D293-8E48-14FE2E4DA212"; // 在接受响应上写 +const AN_DEVICE_FIRMWARE_UPDATE_CHAR = "00006487-3C17-D293-8E48-14FE2E4DA212"; // 接受控制指令的通道 写/通知 + +// DFU Opcode +const OPCODE_DFU_START_DFU = 0x01; +const OPCODE_DFU_RECEIVE_FW_IMAGE = 0x02; +const OPCODE_DFU_VALIDATE_FW_IMAGE = 0x03; +const OPCODE_DFU_ACTIVE_IMAGE_RESET = 0x04; +const OPCODE_DFU_RESET_SYSTEM = 0x05; +const OPCODE_DFU_REPORT_RECEIVED_IMAGE_INFO = 0x06; +const OPCODE_DFU_PACKET_RECEIPT_NOTIFICATION_REQUEST = 0x07; +const OPCODE_DFU_RESPONSE_CODE = 0x10; +const OPCODE_DFU_PACKET_RECEIPT_NOTIFICATION = 0x11; + +// Error Codes +const ERROR_STATE_SUCCESS = 0x01; +const ERROR_STATE_OPCODE_NOT_SUPPORTED = 0x02; +const ERROR_STATE_INVALID_PARA = 0x03; +const ERROR_STATE_OPERATION_FAILED = 0x04; +const ERROR_STATE_DATA_SIZE_EXCEEDS = 0x05; +const ERROR_STATE_CRC_ERROR = 0x06; + +// Structures and data definitions +function DEF_RESPONSE_HEADER(opcode, reqOpcode, rspValue) { + this.Opcode = opcode; + this.ReqOpcode = reqOpcode; + this.RspValue = rspValue; +} + +function DFU_START_DFU(opcode, offset, signature, version, checksum, length, otaFlag, reserved8, reservedForAes) { + this.Opcode = opcode; + this.Offset = offset; + this.Signature = signature; + this.Version = version; + this.Checksum = checksum; + this.Length = length; + this.OtaFlag = otaFlag; + this.Reserved8 = reserved8; + this.ReservedForAes = reservedForAes; +} + +function DFU_START_DFU_RESPONSE(opcode, reqOpcode, rspValue) { + this.Opcode = opcode; + this.ReqOpcode = reqOpcode; + this.RspValue = rspValue; +} + +function RECEIVE_FW_IMAGE(opcode, nSignature, nImageUpdateOffset) { + this.Opcode = opcode; + this.NSignature = nSignature; + this.NImageUpdateOffset = nImageUpdateOffset; +} + +function RECEIVE_FW_IMAGE_RESPONSE(opcode, reqOpcode, rspValue) { + this.Opcode = opcode; + this.ReqOpcode = reqOpcode; + this.RspValue = rspValue; +} + +function VALIDATE_FW_IMAGE(opcode, nSignature) { + this.Opcode = opcode; + this.NSignature = nSignature; +} + +function VALIDATE_FW_IMAGE_RESPONSE(opcode, reqOpcode, rspValue) { + this.Opcode = opcode; + this.ReqOpcode = reqOpcode; + this.RspValue = rspValue; +} + +function ACTIVE_IMAGE_RESET(opcode) { + this.Opcode = opcode; +} + +function RESET_SYSTEM(opcode) { + this.Opcode = opcode; +} + +function REPORT_RECEIVED_IMAGE_INFO(opcode, nSignature) { + this.Opcode = opcode; + this.NSignature = nSignature; +} + +function REPORT_RECEIVED_IMAGE_INFO_RESPONSE(opcode, reqOpcode, rspValue, originalFWVersion, nImageUpdateOffset) { + this.Opcode = opcode; + this.ReqOpcode = reqOpcode; + this.RspValue = rspValue; + this.OriginalFWVersion = originalFWVersion; + this.NImageUpdateOffset = nImageUpdateOffset; +} + +function DFU_FW_IMAGE_INFO(FWImageSize, FWVersion, OriginalVersion, ImageUpdateOffset) { + this.FWImageSize = FWImageSize; + this.FWVersion = FWVersion; + this.OriginalVersion = OriginalVersion; + this.ImageUpdateOffset = ImageUpdateOffset; +} + +function DFU_DATA_INFO(flag, curInfo, imgInfo) { + this.Flag = flag; + this.CurInfo = curInfo; + this.ImgInfo = imgInfo; +} + +function IMAGE_HEADER(offset, signature, version, checksum, length, otaFlag, reserved8) { + this.Offset = offset; + this.Signature = signature; + this.Version = version; + this.Checksum = checksum; + this.Length = length; + this.OtaFlag = otaFlag; + this.Reserved8 = reserved8; +} \ No newline at end of file diff --git a/utils/ParseDataPacket.js b/utils/ParseDataPacket.js new file mode 100644 index 0000000..6d37cf0 --- /dev/null +++ b/utils/ParseDataPacket.js @@ -0,0 +1,543 @@ +const { validateCRC } = require('./crc'); +const CommonUtil = require('./CommonUtil'); +const parseDataPacket = { + + /** + * 时间转时间戳 + * @param {*} year + * @param {*} month + * @param {*} day + * @param {*} hours + * @param {*} minutes + * @param {*} seconds + */ + dateFormat(year,month,day,hours,minutes,seconds){ + + year = parseInt(year); + month = parseInt(month); + day = parseInt(day); + hours = parseInt(hours); + minutes = parseInt(minutes); + seconds = parseInt(seconds); + // 创建 Date 对象 + const date = new Date(2000 + year,month-1, day, hours, minutes, seconds); + // 获取时间戳(单位:秒) + const timestamp = Math.floor(date.getTime() / 1000); // 转换为秒 + return timestamp; + }, + + /** + * level<=1:深睡 + * level==2:浅睡 + * level==3:REM + * level>=4:清醒 + * @param {*} status + */ + mapSleepStatus(status) { + switch (status) { + case 1: + case 0: + return 2; // 深睡 + case 2: + return 1; // 浅睡 + case 3: + return 4; // REM + case 4: + default: + return 3; // 清醒 + } + }, + + /** + * 将字节数组按照小端模式(Little Endian)解析为整数 + * @param {DataView} dataView - DataView 对象 + * @param {number} offset - 解析起始位置 + * @returns {number} 解析后的整数值 + */ + convertToDecimal(dataView, offset) { + // 读取 4 个字节,并按照小端模式进行转换 + return dataView.getUint32(offset, true); // true 代表小端模式 + }, + + /** + * 解析步数总数包(27字节),包括头部和数据部分 + */ + parseStepItem(dataView, offset) { + // 解析头部信息 + const id = dataView.getUint8(offset + 1); // ID: 倒数第X天的总数据,0 表示当天 + const year = dataView.getUint8(offset + 2).toString(16); // 年份(BCD格式) + const month = dataView.getUint8(offset + 3).toString(16); // 月份(BCD格式) + const day = dataView.getUint8(offset + 4).toString(16); // 日期(BCD格式) + + // 解析数据部分 + const steps = this.convertToDecimal(dataView, offset + 5); // 步数(高位在后),单位:步数 + const exerciseTime = this.convertToDecimal(dataView, offset + 9); // 运动时间(高位在后),单位:秒 + const distance = this.convertToDecimal(dataView, offset + 13) * 0.01; // 距离(高位在后),单位:KM + const calories = this.convertToDecimal(dataView, offset + 17) * 0.01; // 卡路里(高位在后),单位:KCAL + const target = dataView.getUint16(offset + 21, true); // 目标(高位在后),单位:目标数,使用小端模式 + const fastExerciseTime = this.convertToDecimal(dataView, offset + 23); // 快速运动时间(高位在后),单位:分钟 + + // 输出解析结果 + console.log(`ID: ${id}, Date: ${year}-${month}-${day}, Steps: ${steps}, Exercise Time: ${exerciseTime} secs, Distance: ${distance} KM, Calories: ${calories} KCAL, Target: ${target}, Fast Exercise Time: ${fastExerciseTime} mins`); + + return { + id, + date: { + year, + month, + day + }, + steps, + exerciseTime, + distance, + calories, + target, + fastExerciseTime + }; + }, + + /** + * 解析整个数据包,逐个处理每个 27 字节的记录 + */ + parseStepList(dataView, startOffset) { + let results = []; + const totalLength = dataView.byteLength; + + // 每个数据包 27 字节,因此以 27 字节为单位拆分 + for (let offset = startOffset; offset + 26 <= totalLength; offset += 27) { + const dataPacket = this.parseStepItem(dataView, offset); // 解析每个 27 字节的数据包 + results.push(dataPacket); + } + + return results; + }, + + /** + * 解析步数详情包(25字节) + */ + parseStepDetailItem(dataView, offset) { + // 解析头部信息 + const id = dataView.getUint16(offset + 1,true); // ID: 倒数第X天的总数据,0 表示当天 + // 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串 + const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式) + const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式) + const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式) + const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式) + const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式) + const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式) + + let time = this.dateFormat(year,month,day,hour,minute,second); + // 解析数据部分 + const steps = dataView.getUint16(offset + 9,true);; // 步数(高位在后)单位:步数 + const calories = dataView.getUint16(offset + 11,true); // 卡路里(高位在后),单位:KCAL + const distance = dataView.getUint16(offset + 13,true); // 距离(高位在后),单位:KM + + return { + calories:calories, + steps:steps, + distance:distance, + startTime:time, + endTime:time + (10 * 60), + stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}` + }; + }, + + + /** + * 解析整个数据包,逐个处理每个 25 字节的记录 + */ + parseStepDetailList(dataView, startOffset) { + let results = []; + const totalLength = dataView.byteLength; + for (let offset = startOffset; offset + 24 <= totalLength; offset += 25) { + const dataPacket = this.parseStepDetailItem(dataView, offset); + results.push(dataPacket); + } + + return results; + }, + + + /** + * 解析心率、血氧数据包,每个是10字节 + * @param {*} dataView + * @param {*} offset + */ + parseDataItem(dataView, offset) { + + let type = 1; + const cmdType = dataView.getUint8(offset); // 指令 + switch (cmdType) { + case 0x55: + type = 1; + break; + case 0x66: + type = 3; + break; + default: + console.warn('未知数据类型', cmdType); + break; + } + + // 解析 ID1 和 ID2(数据编号,高位在后) + const id1 = dataView.getUint8(offset + 1); // ID1 + const id2 = dataView.getUint8(offset + 2); // ID2 + const id = (id2 << 8) | id1; // 组合成一个16位的ID(小端模式) + + // 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串 + const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式) + const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式) + const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式) + const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式) + const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式) + const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式) + + let time = this.dateFormat(year,month,day,hour,minute,second); + // 解析心率值(HH) + const value = dataView.getUint8(offset + 9); // 心率值(单位:BPM),血氧,体温 + + return { + type:type,//1.心率,2,血压,3,血氧,4.体温,5.呼吸率,6,步数 + oneValue:value, + time:time, + stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}` + }; + }, + + /** + * 解析整个数据包,逐个处理每个数据项 + */ + parseDataList(dataView, startOffset) { + let results = []; + const totalLength = dataView.byteLength; + + // 每个数据包 10 字节,因此以 10 字节为单位拆分 + for (let offset = startOffset; offset + 9 <= totalLength; offset += 10) { + const dataPacket = this.parseDataItem(dataView, offset); // 解析每个 10 字节的数据包 + results.push(dataPacket); + } + + return results; + }, + + /** + * 解析体温数据包,每个是11字节 + * @param {*} dataView + * @param {*} offset + */ + parseTempItem(dataView, offset) { + // 解析 ID1 和 ID2(数据编号,高位在后) + const id = dataView.getUint16(offset + 1, true); // id + + // 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串 + const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式) + const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式) + const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式) + const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式) + const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式) + const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式) + + let time = this.dateFormat(year,month,day,hour,minute,second); + // 解析体温值(T1 和 T2) + const t1 = dataView.getUint16(offset + 9, true); // 小端数据,保留一位小数 + + // 计算温度值,T1T2组合成一个16位数字,并转为浮动的值 + const temperature = t1 / 10.0; // 保留1位小数 + + return { + type: 4,//4.体温 + oneValue:temperature, + time:time, + stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}` + }; + }, + + /** + * 解析体温整个数据包,逐个处理每个数据项 + */ + parseTempList(dataView, startOffset) { + let results = []; + const totalLength = dataView.byteLength; + + // 每个数据包 11 字节,因此以 11 字节为单位拆分 + for (let offset = startOffset; offset + 10 <= totalLength; offset += 11) { + const dataPacket = this.parseTempItem(dataView, offset); // 解析每个 10 字节的数据包 + results.push(dataPacket); + } + + return results; + }, + + /** + * 解析血压数据包,每个是15字节 + * @param {*} dataView + * @param {*} offset + */ + parseBloodPressureItem(dataView, offset) { + // 解析 ID1 和 ID2(数据编号,高位在后) + const id = dataView.getUint16(offset + 1,true); + + // 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串 + const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式) + const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式) + const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式) + const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式) + const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式) + const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式) + + let time = this.dateFormat(year,month,day,hour,minute,second); + // 解析血压->高压 + const high = dataView.getUint8(offset + 13); + // 解析血压->高压 + const low = dataView.getUint8(offset + 14); + + return { + type: 2,//2.血压 + oneValue:high, + twoValue:low, + time:time, + stringTime:`${year}-${month}-${day} ${hour}:${minute}:${second}` + }; + }, + + /** + * 解析体温整个数据包,逐个处理每个数据项 + */ + parseBloodPressureList(dataView, startOffset) { + let results = []; + const totalLength = dataView.byteLength; + + // 每个数据包 15 字节,因此以 15 字节为单位拆分 + for (let offset = startOffset; offset + 14 <= totalLength; offset += 15) { + const dataPacket = this.parseBloodPressureItem(dataView, offset); // 解析每个 15 字节的数据包 + results.push(dataPacket); + } + + return results; + }, + /** + * 解析睡眠数据包,每个是11字节 + * @param {*} dataView + * @param {*} offset + */ + parseSleepItem(dataView, offset) { + // 解析 ID1 和 ID2(数据编号,高位在后) + const id1 = dataView.getUint8(offset + 1); // ID1 + + // 解析日期时间(YY MM DD HH mm SS)并转换为16进制字符串 + const year = dataView.getUint8(offset + 3).toString(16).padStart(2, '0'); // 年份(BCD格式) + const month = dataView.getUint8(offset + 4).toString(16).padStart(2, '0'); // 月份(BCD格式) + const day = dataView.getUint8(offset + 5).toString(16).padStart(2, '0'); // 日期(BCD格式) + const hour = dataView.getUint8(offset + 6).toString(16).padStart(2, '0'); // 小时(BCD格式) + const minute = dataView.getUint8(offset + 7).toString(16).padStart(2, '0'); // 分钟(BCD格式) + const second = dataView.getUint8(offset + 8).toString(16).padStart(2, '0'); // 秒(BCD格式) + + // 解析睡眠长度 + const length = dataView.getUint8(offset + 9, true); + + // 解析每一分钟的睡眠质量数据(SD1 到 SD120),最多 120 个数据 + const sleepData = []; + for (let i = 0; i < length; i++) { + sleepData.push(dataView.getUint8(offset + 10 + i)); // 每分钟的睡眠质量 + } + + let time = this.dateFormat(year,month,day,hour,minute,second); + // 获取当天00:00:00的时间戳 + const midnight = this.dateFormat(year,month,day,0,0,0); + let startMinutesOfDay = Math.floor((time - midnight) / 60); // 计算从当天00:00:00开始到当前时间的分钟数 + + // 用于存储转换后的结果 + let result = []; + let currentMinute = startMinutesOfDay; // 起始分钟(418分钟) + + // 开始循环遍历 sleepData 数据 + for (let i = 0; i < sleepData.length; i++) { + const status = sleepData[i]; + + // 处理每分钟的睡眠数据 + result.push({ + minute: currentMinute + 1, // 分钟递增 + status: this.mapSleepStatus(status), // 映射睡眠状态 + yyyyMMdd: `${parseInt(year) + 2000}${month}${day}`, // 格式化为yyyyMMdd + dateTime: `${parseInt(year)}-${month}-${day} ${hour}:${minute}:${second}`, // 格式化为yyyyMMdd + time: time + }); + + // 递增分钟数 + currentMinute++; + } + return result; + + }, + + /** + * 解析睡眠整个数据包,逐个处理每个数据项 + */ + parseSleepList(dataView, startOffset) { + + const totalLength = dataView.byteLength; + + // 每个数据包 130 字节,因此以 130 字节为单位拆分 + for (let offset = startOffset; offset + 129 <= totalLength; offset += 130) { + const dataPacket = this.parseSleepItem(dataView, offset); // 解析每个 130 字节的数据包 + return dataPacket; + } + }, + + + + /** + * 解析手环基本参数 + */ + parseWatchBasic(dataView, offset){ + + // AA:距离单位:0x01:MILE 0x00:KM + // BB:12小时24小时显示:0x01: 12小时制,0x00:24小时制度。 + // CC:抬手检查使能标志, 0x01:使能, 0x00:关闭 + // DD:温度单位切换: 0x01:华摄氏度 ,0x00:摄氏度 默认摄氏度 + // EE:夜间模式: 0x01开启 , 0x00:关闭(夜间模式开启后晚上六点到凌晨7点,自动调节显示亮度到最暗) + // FF:ANCS使能开关: 0x01:使能,0x00:关闭 + // II:基础心率设置 + // JJ:保留 + // KK:屏幕亮度调节,调节范围80-8f(0为最亮,f为最暗)(0~15:值越大则越暗) + // LL:表盘界面 + // MM: 社交距离开关 0x01 开 0x00 关 + // NN:中英文切换, 0代表英文(默认), 1代表中文 + const distanceFlag = dataView.getUint8(offset + 1); + const timeFlag = dataView.getUint8(offset + 2); + const WRSFlag = dataView.getUint8(offset + 3); + const tempFlag = dataView.getUint8(offset + 4); + const nightModeFlag = dataView.getUint8(offset + 5); + const ANCSFlag = dataView.getUint8(offset + 6); + const heartRateWarningFlag = dataView.getUint8(offset + 7); + const JJ = dataView.getUint8(offset + 10); + const KK = dataView.getUint8(offset + 11); + const LL = dataView.getUint8(offset + 12); + const MM = dataView.getUint8(offset + 13); + const NN = dataView.getUint8(offset + 14); + + return { + distanceFlag:distanceFlag,timeFlag:timeFlag,WRSFlag:WRSFlag,tempFlag:tempFlag, + nightModeFlag:nightModeFlag,ANCSFlag:ANCSFlag,heartRateWarningFlag:heartRateWarningFlag, + JJ:JJ,SBLevel:CommonUtil.respSBLevelConv(KK),LL:LL,MM:MM,NN:NN + } + }, + + /** + * 解析闹钟数据包,每个是41字节 + * @param {*} dataView + * @param {*} offset + */ + parseAlarmClockItem(dataView, offset) { + const id = dataView.getUint16(offset + 1, true); // id + if (id === 0x57FF) { + return; + } + const count = dataView.getUint8(offset + 3); // 闹钟个数 + const number = dataView.getUint8(offset + 4); // 闹钟编号 + const enable = dataView.getUint8(offset + 5); // 闹钟状态 0:关闭,1:开启 + const type = dataView.getUint8(offset + 6); // 闹钟类型 1:闹钟,2:吃药提示,3:喝水提示 4:吃饭闹钟 5:洗手闹钟 + const hour = dataView.getUint8(offset + 7).toString(16); // 闹钟:时 + const minute = dataView.getUint8(offset + 8).toString(16);// 闹钟:分 + const week = dataView.getUint8(offset + 9);// 周,8bits,表示周一到周天 + const length = 30; // 长度,30个字节是闹钟内容,目前发送了手表端不会显示,暂不处理 + // 解析星期使能位 + const weekDays = [ + (week & 0x01) !== 0 ? 1 : 0, // Sunday + (week & 0x02) !== 0 ? 1 : 0, // Monday + (week & 0x04) !== 0 ? 1 : 0, // Tuesday + (week & 0x08) !== 0 ? 1 : 0, // Wednesday + (week & 0x10) !== 0 ? 1 : 0, // Thursday + (week & 0x20) !== 0 ? 1 : 0, // Friday + (week & 0x40) !== 0 ? 1 : 0 // Saturday + ]; + return { + count: count, + number:number, + enable:enable, + type:type, + hour:hour, + minute:minute, + weekDays:weekDays, + // textContent:textContent + }; + }, + + /** + * 解析闹钟整个数据包,逐个处理每个数据项 + */ + parseAlarmClockList(dataView, startOffset) { + let results = []; + const totalLength = dataView.byteLength; + + // 每个数据包 41 字节,因此以 41 字节为单位拆分 + for (let offset = startOffset; offset + 40 <= totalLength; offset += 41) { + const dataPacket = this.parseAlarmClockItem(dataView, offset); // 解析每个 41 字节的数据包 + results.push(dataPacket); + } + + return results; + }, + + /** + * 解析久坐数据 + * @param {*} dataView + * @param {*} offset + */ + parseSedentary(dataView, offset){ + const startHour = dataView.getUint8(offset + 1); // 开始运动时间 小时 + const startMinute = dataView.getUint8(offset + 2); + const endHour =dataView.getUint8(offset + 3); + const endMinute = dataView.getUint8(offset + 4); + const week = dataView.getUint8(offset + 5); + const reminderInterval = dataView.getUint8(offset + 6); + const minSteps = dataView.getUint8(offset + 7); + const sportSwitch = dataView.getUint8(offset + 8); + + // 解析星期使能位 + const weekDays = [ + (week & 0x01) !== 0 ? 1 : 0, // Sunday + (week & 0x02) !== 0 ? 1 : 0, // Monday + (week & 0x04) !== 0 ? 1 : 0, // Tuesday + (week & 0x08) !== 0 ? 1 : 0, // Wednesday + (week & 0x10) !== 0 ? 1 : 0, // Thursday + (week & 0x20) !== 0 ? 1 : 0, // Friday + (week & 0x40) !== 0 ? 1 : 0 // Saturday + ]; + return { + startHour: startHour, + startMinute:startMinute, + endHour:endHour, + endMinute:endMinute, + weekDays:weekDays, + reminderInterval:reminderInterval, + minSteps:minSteps, + sportSwitch:sportSwitch + }; + }, + + + /** + * 运动目标解析 + * @param {*} dataView + * @param {*} offset + */ + parseStepTarget(dataView, offset){ + const targetSteps = dataView.getUint32(offset + 1,true); + const targetTime = dataView.getUint16(offset + 5,true); + const targetDistance = dataView.getUint16(offset + 7,true); + const targetCalories = dataView.getUint16(offset + 9,true); + const targetSleep = dataView.getUint16(offset + 11,true); + return { + targetSteps:targetSteps, + targetTime:targetTime, + targetDistance:targetDistance, + targetCalories:targetCalories, + targetSleep:targetSleep + } + } + +} + +// 导出模块 +module.exports = parseDataPacket; \ No newline at end of file diff --git a/utils/SendCommandUtil.js b/utils/SendCommandUtil.js new file mode 100644 index 0000000..a73a7ee --- /dev/null +++ b/utils/SendCommandUtil.js @@ -0,0 +1,27 @@ +// 发送指令工具类 +const serviceId = "0000FFF0-0000-1000-8000-00805F9B34FB"; //写服务ID +const writeCharacteristicId = "0000FFF6-0000-1000-8000-00805F9B34FB"; //写特征ID +const SendCommandUtil = { + sendPackage(deviceId, packageData){ + // 通过蓝牙向设备写入命令 + wx.writeBLECharacteristicValue({ + deviceId: deviceId, // 蓝牙设备 ID + serviceId: serviceId, // 蓝牙服务 UUID + characteristicId: writeCharacteristicId, // 蓝牙特征 UUID + value: packageData.buffer, // 发送的命令缓冲区 + success: (res) => { + // console.log('命令发送成功', res); + }, + fail: (err) => { + console.error('命令发送失败', err); + }, + complete: (res) => { + // console.log('接口调用结束', res); + }, + }); + }, + +}; + +// 导出模块 +module.exports = SendCommandUtil; \ No newline at end of file diff --git a/utils/WeatherUtils.js b/utils/WeatherUtils.js new file mode 100644 index 0000000..557a73f --- /dev/null +++ b/utils/WeatherUtils.js @@ -0,0 +1,61 @@ +// WeatherUtils.js +const WeatherEnum = { + 0: '晴', + 4: '多云', + 5: '晴间多云', + 6: '晴间多云', + 7: '大部多云', + 8: '大部多云', + 9: '阴', + 10: '阵雨', + 11: '雷阵雨', + 12: '雷阵雨伴有冰雹', + 13: '小雨', + 14: '中雨', + 15: '大雨', + 16: '暴雨', + 17: '大暴雨', + 18: '特大暴雨', + 19: '冻雨', + 20: '雨夹雪', + 21: '阵雪', + 22: '小雪', + 23: '中雪', + 24: '大雪', + 25: '暴雪', + 26: '浮尘', + 27: '扬沙', + 28: '沙尘暴', + 29: '强沙尘暴', + 30: '雾', + 31: '霾', + 32: '风', + 33: '大风', + 34: '飓风', + 35: '热带风暴', + 36: '龙卷风', + 37: '冷', + 38: '热', + // 0xfe: '查询更新时间', + 0x99: '未知' +}; + +class WeatherUtils { + // 根据描述获取对应的天气编码 + static getWeatherCode(description) { + for (const [code, desc] of Object.entries(WeatherEnum)) { + if (desc === description) { + return parseInt(code); // 返回对应的编码 + } + } + return null; // 如果没有找到,返回 null + } + + // 根据编码获取对应的天气描述 + static getWeatherDescription(code) { + return WeatherEnum[code] || '未知'; // 返回对应的描述,如果没有找到返回 '未知' + } +} + +// 导出工具类 +module.exports = WeatherUtils; diff --git a/utils/crc.js b/utils/crc.js new file mode 100644 index 0000000..69da32d --- /dev/null +++ b/utils/crc.js @@ -0,0 +1,22 @@ +// CRC 校验模块 + +// 计算数据包的 CRC 校验值 +function calculateCRC(dataPacket) { + let sum = 0; + for (let i = 0; i < 15; i++) { + sum += dataPacket[i]; + } + return sum & 0xFF; // 取最低8位 +} + +// 校验CRC值 +function validateCRC(dataPacket) { + const receivedCRC = dataPacket[15]; + const calculatedCRC = calculateCRC(dataPacket); + return receivedCRC === calculatedCRC; +} + +module.exports = { + calculateCRC, + validateCRC +};