/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

/*
 * Implementation of the Secure Hashing Algorithm (SHA-384)
 *
 * Generates a 384 bit message digest. It should be impossible to come
 * come up with two messages that hash to the same value ("collision free").
 *
 * For use with byte-oriented messages only.
 */

//nhs_package main

package amcl

//import "fmt"

const NHS_PRIME int32 = 0x3001   // q in Hex
const NHS_LGN uint = 10          // Degree n=2^LGN
const NHS_ND uint32 = 0xF7002FFF // 1/(R-q) mod R
const NHS_ONE int32 = 0x2AC8     // R mod q
const NHS_R2MODP uint64 = 0x1620 // R^2 mod q

const NHS_DEGREE int = (1 << NHS_LGN)
const NHS_WL uint = 32

const NHS_inv int32 = 0xeab
const NHS_invpr int32 = 0x2c2a

var NHS_roots = [1024]int32{0x2ac8, 0x2baf, 0x299b, 0x685, 0x2f04, 0x158d, 0x2d49, 0x24b5, 0x1edc, 0xab3, 0x2a95, 0x24d, 0x3cb, 0x6a8, 0x12f9, 0x15ba, 0x1861, 0x2a89, 0x1c5c, 0xbe6, 0xc1e, 0x2024, 0x207, 0x19ce, 0x2710, 0x1744, 0x18bc, 0x2cd7, 0x396, 0x18d5, 0x1c45, 0xc4, 0x21a6, 0xe03, 0x2b3c, 0x2d91, 0xc5d, 0x432, 0x1fbc, 0xcae, 0x2512, 0x2979, 0x3b2, 0x714, 0xb2e, 0x1a97, 0x1a03, 0x1bcd, 0x2216, 0x2701, 0xa, 0x263c, 0x1179, 0x200c, 0x2d08, 0x1c34, 0x291, 0x2c99, 0x2a5a, 0x723, 0xb1d, 0x1ccc, 0x1fb6, 0x2f58, 0x2bfe, 0x1cda, 0x2a0, 0x5f1, 0x2de, 0x1fc7, 0x1ea8, 0x1719, 0x2fa7, 0x27ec, 0x20ff, 0x12c0, 0x1ac1, 0x2232, 0x2f9b, 0xd3e, 0x2aed, 0x15f0, 0x11e8, 0xed0, 0x26a, 0x1de5, 0xa3f, 0xf43, 0xebf, 0x204e, 0xac7, 0x2d9c, 0x5ea, 0x25d1, 0xb6, 0x49c, 0x995, 0x2555, 0x26e2, 0x100, 0x1878, 0x5aa, 0x2e10, 0x271c, 0xcb, 0x1b4c, 0x2fb8, 0x25b7, 0x1543, 0x2c7b, 0x241a, 0x2223, 0x20ca, 0x24ed, 0x137, 0x1b65, 0x1dc2, 0x7c7, 0x2ec3, 0xd0c, 0x1169, 0x1c7a, 0x1ea1, 0xf89, 0x2199, 0x291d, 0x1088, 0x2046, 0x256d, 0x2bc7, 0x2e9b, 0x41f, 0x1b55, 0x2b38, 0xd0, 0x2e6a, 0x1755, 0x6bc, 0x2724, 0x3ba, 0x222e, 0x2c5c, 0x2da5, 0x213c, 0x10fe, 0x169a, 0x1552, 0x5d3, 0x300, 0x1b5d, 0x1342, 0x2004, 0x256f, 0x2039, 0x667, 0x23b5, 0x1123, 0xdb, 0x2da0, 0xe1e, 0x2f54, 0x2767, 0x154a, 0x40a, 0x11d3, 0x2821, 0xc09, 0x974, 0x694, 0xfbf, 0x27ba, 0x132, 0x83f, 0x2d06, 0x10e, 0x183f, 0x29ae, 0x28c3, 0x2dc9, 0x1144, 0x2c70, 0x2a4a, 0xf3c, 0x1e32, 0x1171, 0x1e43, 0xdd4, 0x2ddf, 0x28d2, 0xfac, 0x3c4, 0x2f19, 0x10a6, 0x2f7, 0xe1d, 0x828, 0x138f, 0x1332, 0xfab, 0xcf6, 0x13f8, 0x24a0, 0x112d, 0x2717, 0x6e7, 0x1044, 0x36e, 0xfe8, 0x6a, 0xba7, 0x1d69, 0x29ec, 0x23b2, 0xaee, 0x16df, 0x1068, 0x1a7e, 0x253f, 0x24c, 0xb33, 0x2683, 0x15ce, 0x1ad3, 0x1a36, 0xc96, 0xaea, 0x260a, 0xce, 0x28b1, 0xe4f, 0x2b11, 0x5f8, 0x1fc4, 0xe77, 0x2366, 0x11f9, 0x153c, 0x24eb, 0x20cd, 0x1398, 0x22, 0x2b97, 0x249b, 0x8eb, 0x12b2, 0x2fe3, 0x29c1, 0x1b00, 0x2663, 0xeaa, 0x2e06, 0xe0, 0x1569, 0x10f5, 0x284e, 0xa38, 0x201d, 0x1c53, 0x1681, 0x1f6f, 0x2f95, 0x2fe8, 0xacb, 0x1680, 0x17fd, 0x2c39, 0x165a, 0x10bb, 0x29d8, 0x2622, 0x1196, 0x884, 0x2a79, 0x140e, 0x2d80, 0x6fa, 0x11b2, 0x26c4, 0x355, 0x1054, 0x29e9, 0x23ed, 0xbe3, 0x24fa, 0x1fb3, 0x10ac, 0x2919, 0x2584, 0x10a4, 0xe85, 0x650, 0x1893, 0x1dc1, 0xd8e, 0x12dc, 0x2d42, 0x284d, 0xfff, 0x250f, 0xacd, 0x13c3, 0x6cc, 0x1a79, 0x1221, 0x2614, 0x270a, 0x1ea, 0x155, 0x2818, 0x222c, 0x2e5b, 0x25d8, 0x1dbf, 0x191c, 0xb0f, 0xdac, 0x1082, 0x12ef, 0x11b6, 0xfa8, 0x2b72, 0x159d, 0x209e, 0x31b, 0x2c7c, 0x14f7, 0xe09, 0x1bb2, 0x1ec7, 0x2404, 0x20ae, 0x6ad, 0xed6, 0x2b70, 0x1c7b, 0x18d1, 0x2732, 0x12da, 0xd56, 0x5c1, 0x1648, 0x18b7, 0x1605, 0x1bc4, 0x280, 0x2ece, 0xc, 0x1aae, 0x1c4, 0x1cdb, 0x22d6, 0x21d8, 0x257c, 0x51f, 0x211b, 0xff, 0x2ee0, 0x2585, 0xe1, 0x2c35, 0x26db, 0x2971, 0x2208, 0x17e1, 0x21be, 0x135e, 0x28d6, 0x2891, 0x1689, 0x2138, 0xb86, 0x2e3a, 0x1204, 0x2d10, 0x2324, 0xf3f, 0x2508, 0x33d, 0xcb2, 0x292a, 0xe27, 0x2e64, 0x29f8, 0x2d46, 0x9b7, 0x20eb, 0x1b7c, 0x9eb, 0x2b2a, 0x58c, 0x27d0, 0x121b, 0x272e, 0x29f6, 0x2dbd, 0x2697, 0x2aac, 0xd6f, 0x1c67, 0x2c5b, 0x108d, 0x363, 0x249d, 0x2d5e, 0x2fd, 0x2cb2, 0x1f8f, 0x20a4, 0xa19, 0x2ac9, 0x19b1, 0x1581, 0x17a2, 0x29eb, 0x1b72, 0x13b0, 0xee4, 0xa8f, 0x2315, 0x5e6, 0x951, 0x2e29, 0xdad, 0x1f2b, 0x224e, 0x37f, 0x1a72, 0xa91, 0x1407, 0x2df9, 0x3ad, 0x23f7, 0x1a24, 0x1d2a, 0x234b, 0x1df3, 0x1143, 0x7ff, 0x1a6d, 0x2774, 0x2690, 0x2ab5, 0x586, 0x2781, 0x2009, 0x2fdd, 0x2881, 0x399, 0x2fb6, 0x144, 0x137f, 0xfa0, 0x2e4c, 0x1c7f, 0x2fac, 0xb09, 0x1264, 0x127b, 0x198c, 0x2b40, 0x230, 0x1cf4, 0x180b, 0xb58, 0x144a, 0x2aec, 0xfb, 0x2602, 0x14ee, 0x783, 0x1098, 0x23d8, 0x203, 0xe9, 0x108a, 0x14b8, 0xeec, 0xc58, 0x1248, 0x243c, 0x28aa, 0x6bf, 0x27c4, 0x276e, 0x19b8, 0x1d11, 0x2e16, 0x472, 0x1464, 0x24b9, 0x662, 0x1097, 0x2067, 0x20d6, 0x171c, 0x4, 0x682, 0x17bb, 0x1186, 0x4f2, 0x3ff, 0x2a43, 0x1dc7, 0x1ae5, 0x8cc, 0x2e7c, 0x2ef8, 0x2ae0, 0x2904, 0xed4, 0x6c5, 0x14ae, 0xb72, 0x11c3, 0x337, 0x2da3, 0x2916, 0x6d8, 0x1cf9, 0x10ee, 0x1800, 0x1ae4, 0xa0d, 0x101b, 0x1a8d, 0x2e98, 0x24cd, 0x813, 0x1aa4, 0x9b9, 0x680, 0x2349, 0x24d1, 0x20f8, 0xe31, 0x249f, 0x216b, 0x12d9, 0x1d21, 0x19db, 0x191a, 0x1dd0, 0x5df, 0x55c, 0x2b86, 0x213, 0xe9e, 0x1ef1, 0x268a, 0x1d5e, 0x1e20, 0x28c1, 0x1379, 0x249, 0x19de, 0x18b, 0x1e41, 0x2a1e, 0x2612, 0x297, 0x2e96, 0x2102, 0x46, 0x1b9f, 0x1a4d, 0x2050, 0x1b32, 0x568, 0x11f7, 0x1829, 0x870, 0x1f4, 0x1dca, 0x990, 0x1df6, 0x2b62, 0x13ec, 0x9f2, 0x1260, 0x2997, 0x1412, 0x1e6d, 0x1694, 0x11ac, 0x2d8b, 0x276f, 0x26f5, 0x233e, 0x2b44, 0x2f5a, 0x2d37, 0x2cb1, 0xc75, 0x98d, 0x1d56, 0x7ae, 0x10e6, 0x113f, 0x17b8, 0xad3, 0x737, 0x221e, 0x1b70, 0x1f3e, 0x2966, 0x18b2, 0x4fa, 0x2044, 0x1312, 0x154e, 0x2029, 0x700, 0x1b45, 0x27a6, 0x226a, 0x21bf, 0x58d, 0x2f11, 0x2e02, 0x17fc, 0x4d2, 0x1757, 0xcb1, 0x2ef1, 0x2582, 0x1276, 0x881, 0x2fc0, 0x104a, 0x670, 0x274f, 0x2b53, 0x19dd, 0x752, 0x1663, 0xcbd, 0x2b2b, 0x2fc6, 0x13b6, 0x21e6, 0x15f6, 0x126b, 0x2637, 0x1cd9, 0x2f50, 0xe82, 0x5b0, 0x24e0, 0x1350, 0x2f24, 0x21f7, 0x1a16, 0x2f3e, 0x167e, 0x1f7d, 0x28a0, 0x16f0, 0xe33, 0x53b, 0x28c5, 0x1500, 0x2f88, 0x26cc, 0x2018, 0x1604, 0x218b, 0x2cd1, 0x9ee, 0x17f3, 0x5fd, 0x1f5a, 0x2d0, 0x2b46, 0x23cc, 0x503, 0x1c46, 0x1cc3, 0x28e2, 0x243e, 0x122b, 0x2e0c, 0xe37, 0x2611, 0x85e, 0x9b8, 0x1b24, 0x762, 0x19b6, 0x3bc, 0x2d50, 0x2079, 0x18da, 0x170a, 0x800, 0xaa2, 0x135a, 0x1a15, 0x13d1, 0xca, 0x2113, 0x2db9, 0xdb2, 0x1a5c, 0x29a9, 0x1488, 0x14c1, 0x2c9, 0x917, 0x28e7, 0x265c, 0xdab, 0x2ab9, 0x2bc6, 0x105b, 0x1839, 0x219c, 0x50, 0x11da, 0x1802, 0xf56, 0x2e6, 0x2190, 0xddb, 0x56e, 0x9d9, 0x1c81, 0x1016, 0x12d6, 0x296f, 0x14b4, 0x1014, 0x1e64, 0x1d90, 0x89f, 0x2bc2, 0x2777, 0x2819, 0x1c65, 0x1a41, 0x5a2, 0x2cd2, 0x427, 0xd71, 0x29c8, 0x1e58, 0x53f, 0x7c5, 0x1dcd, 0x4a1, 0x1268, 0x2597, 0x2926, 0xee, 0x111b, 0x1038, 0xe6c, 0x22dc, 0x2f2f, 0x441, 0x2cfd, 0x1cb0, 0x6a4, 0x2224, 0x620, 0x5dc, 0x16b1, 0x2a1d, 0x1787, 0x20c7, 0x641, 0xd84, 0x1c05, 0x2d0d, 0x2f52, 0x1b8c, 0xd7d, 0x17e8, 0x1589, 0xc73, 0x151b, 0x4e2, 0x1ae9, 0x1b18, 0xb9b, 0x949, 0x2c60, 0x1e7a, 0xd5, 0x1bdc, 0x1f57, 0x1753, 0x124a, 0x559, 0xb76, 0x2334, 0x12d1, 0x1de1, 0x14b2, 0x2faa, 0x1697, 0x147a, 0x5a1, 0x2c30, 0x1c02, 0x1043, 0x2ee1, 0x2402, 0x1cc8, 0x2a16, 0xff7, 0x1364, 0x1b9a, 0x2a53, 0x2f94, 0x294c, 0x1ee5, 0x1a87, 0x2141, 0xd66, 0x953, 0x28a3, 0x2f30, 0x2477, 0x18e3, 0x1035, 0x1fc1, 0x1d68, 0x2fb3, 0x138c, 0x2487, 0x1bf8, 0xd96, 0x1018, 0x748, 0x244e, 0x15bd, 0x175e, 0x2be, 0x23d, 0x1da, 0x176d, 0xc17, 0x24be, 0x2ebb, 0x7d8, 0x100a, 0x759, 0x1db4, 0x2259, 0x23f4, 0x2d59, 0x2847, 0xbf5, 0x1cfe, 0xa20, 0x258, 0x1180, 0x279c, 0x54, 0x2abf, 0xc5c, 0x9f9, 0x3d5, 0x2ce4, 0x165f, 0x23d9, 0x27b9, 0x6f9, 0x281a, 0x169e, 0x627, 0x156d, 0x1ff8, 0x211, 0x2e34, 0x1724, 0x2c2e, 0x2790, 0x2dd5, 0x2bf2, 0xdbc, 0x2884, 0x20a9, 0x2390, 0x1e1a, 0x1b6a, 0x5f7, 0xab7, 0x1333, 0x16ab, 0x28dd, 0x20, 0x30f, 0x24b6, 0x5c2, 0x1ce4, 0x1400, 0x2669, 0x60, 0x156c, 0xe20, 0x26d4, 0x26ab, 0x1ebb, 0x223d, 0x5b4, 0x2025, 0x1e1c, 0xaae, 0x2e08, 0x6cd, 0x1677, 0x13d9, 0x17b5, 0x1046, 0x1d8c, 0x14eb, 0x18d8, 0x1ce5, 0x2478, 0x16ae, 0xb79, 0x23d4, 0x684, 0x156b, 0x567, 0x1a, 0x29ce, 0x83a, 0x19e8, 0x58e, 0x294a, 0x1136, 0x2319, 0x2fba, 0x1a29, 0x1d, 0x1879, 0x291b, 0x19f6, 0x2c2f, 0x21c9, 0x19bb, 0xbbc, 0x26f9, 0xc22, 0x708, 0x11a1, 0x18d3, 0x7f8, 0x28f8, 0x2427, 0x1deb, 0xaed, 0x26aa, 0x2482, 0x203b, 0x2f05, 0x2b82, 0x192f, 0x2df4, 0x8dc, 0x2877, 0xd5e, 0x240e, 0x775, 0x2dae, 0x1d3e, 0x20ba, 0x215b, 0x22d1, 0xeba, 0xf50, 0xaa8, 0x184a, 0x1f67, 0x2e04, 0xc6e, 0x6dd, 0x1a09, 0x27f, 0x494, 0x1426, 0xae3, 0xe15, 0x65f, 0x13c4, 0x105, 0x872, 0x2667, 0x1ff6, 0xd9f, 0x2ca1, 0x2f39, 0x2657, 0x23fd, 0x2405, 0xb73, 0x2294, 0x1f1e, 0x2eba, 0x110a, 0x2cae, 0x141f, 0x22cd, 0x25d6, 0x11c1, 0x1c, 0x2d8e, 0x161a, 0x1aa8, 0x229e, 0x1bf9, 0x7cf, 0x106d, 0x2c40, 0xd93, 0x255e, 0x28c2, 0xc1a, 0x2f17, 0x7ca, 0x2f63, 0xbf}
var NHS_iroots = [1024]int32{0x2ac8, 0x452, 0x297c, 0x666, 0xb4c, 0x2b8, 0x1a74, 0xfd, 0x1a47, 0x1d08, 0x2959, 0x2c36, 0x2db4, 0x56c, 0x254e, 0x1125, 0x2f3d, 0x13bc, 0x172c, 0x2c6b, 0x32a, 0x1745, 0x18bd, 0x8f1, 0x1633, 0x2dfa, 0xfdd, 0x23e3, 0x241b, 0x13a5, 0x578, 0x17a0, 0xa9, 0x104b, 0x1335, 0x24e4, 0x28de, 0x5a7, 0x368, 0x2d70, 0x13cd, 0x2f9, 0xff5, 0x1e88, 0x9c5, 0x2ff7, 0x900, 0xdeb, 0x1434, 0x15fe, 0x156a, 0x24d3, 0x28ed, 0x2c4f, 0x688, 0xaef, 0x2353, 0x1045, 0x2bcf, 0x23a4, 0x270, 0x4c5, 0x21fe, 0xe5b, 0xfbb, 0x1f79, 0x6e4, 0xe68, 0x2078, 0x1160, 0x1387, 0x1e98, 0x22f5, 0x13e, 0x283a, 0x123f, 0x149c, 0x2eca, 0xb14, 0xf37, 0xdde, 0xbe7, 0x386, 0x1abe, 0xa4a, 0x49, 0x14b5, 0x2f36, 0x8e5, 0x1f1, 0x2a57, 0x1789, 0x2f01, 0x91f, 0xaac, 0x266c, 0x2b65, 0x2f4b, 0xa30, 0x2a17, 0x265, 0x253a, 0xfb3, 0x2142, 0x20be, 0x25c2, 0x121c, 0x2d97, 0x2131, 0x1e19, 0x1a11, 0x514, 0x22c3, 0x66, 0xdcf, 0x1540, 0x1d41, 0xf02, 0x815, 0x5a, 0x18e8, 0x1159, 0x103a, 0x2d23, 0x2a10, 0x2d61, 0x1327, 0x403, 0x25c9, 0x7b3, 0x1f0c, 0x1a98, 0x2f21, 0x1fb, 0x2157, 0x99e, 0x1501, 0x640, 0x1e, 0x1d4f, 0x2716, 0xb66, 0x46a, 0x2fdf, 0x1c69, 0xf34, 0xb16, 0x1ac5, 0x1e08, 0xc9b, 0x218a, 0x103d, 0x2a09, 0x4f0, 0x21b2, 0x750, 0x2f33, 0x9f7, 0x2517, 0x236b, 0x15cb, 0x152e, 0x1a33, 0x97e, 0x24ce, 0x2db5, 0xac2, 0x1583, 0x1f99, 0x1922, 0x2513, 0xc4f, 0x615, 0x1298, 0x245a, 0x2f97, 0x2019, 0x2c93, 0x1fbd, 0x291a, 0x8ea, 0x1ed4, 0xb61, 0x1c09, 0x230b, 0x2056, 0x1ccf, 0x1c72, 0x27d9, 0x21e4, 0x2d0a, 0x1f5b, 0xe8, 0x2c3d, 0x2055, 0x72f, 0x222, 0x222d, 0x11be, 0x1e90, 0x11cf, 0x20c5, 0x5b7, 0x391, 0x1ebd, 0x238, 0x73e, 0x653, 0x17c2, 0x2ef3, 0x2fb, 0x27c2, 0x2ecf, 0x847, 0x2042, 0x296d, 0x268d, 0x23f8, 0x7e0, 0x1e2e, 0x2bf7, 0x1ab7, 0x89a, 0xad, 0x21e3, 0x261, 0x2f26, 0x1ede, 0xc4c, 0x299a, 0xfc8, 0xa92, 0xffd, 0x1cbf, 0x14a4, 0x2d01, 0x2a2e, 0x1aaf, 0x1967, 0x1f03, 0xec5, 0x25c, 0x3a5, 0xdd3, 0x2c47, 0x8dd, 0x2945, 0x18ac, 0x197, 0x2f31, 0x4c9, 0x14ac, 0x2be2, 0x166, 0x43a, 0xa94, 0x1b53, 0x293c, 0x212d, 0x6fd, 0x521, 0x109, 0x185, 0x2735, 0x151c, 0x123a, 0x5be, 0x2c02, 0x2b0f, 0x1e7b, 0x1846, 0x297f, 0x2ffd, 0x18e5, 0xf2b, 0xf9a, 0x1f6a, 0x299f, 0xb48, 0x1b9d, 0x2b8f, 0x1eb, 0x12f0, 0x1649, 0x893, 0x83d, 0x2942, 0x757, 0xbc5, 0x1db9, 0x23a9, 0x2115, 0x1b49, 0x1f77, 0x2f18, 0x2dfe, 0xc29, 0x1f69, 0x287e, 0x1b13, 0x9ff, 0x2f06, 0x515, 0x1bb7, 0x24a9, 0x17f6, 0x130d, 0x2dd1, 0x4c1, 0x1675, 0x1d86, 0x1d9d, 0x24f8, 0x55, 0x1382, 0x1b5, 0x2061, 0x1c82, 0x2ebd, 0x4b, 0x2c68, 0x780, 0x24, 0xff8, 0x880, 0x2a7b, 0x54c, 0x971, 0x88d, 0x1594, 0x2802, 0x1ebe, 0x120e, 0xcb6, 0x12d7, 0x15dd, 0xc0a, 0x2c54, 0x208, 0x1bfa, 0x2570, 0x158f, 0x2c82, 0xdb3, 0x10d6, 0x2254, 0x1d8, 0x26b0, 0x2a1b, 0xcec, 0x2572, 0x211d, 0x1c51, 0x148f, 0x616, 0x185f, 0x1a80, 0x1650, 0x538, 0x25e8, 0xf5d, 0x1072, 0x34f, 0x2d04, 0x2a3, 0xb64, 0x2c9e, 0x1f74, 0x3a6, 0x139a, 0x2292, 0x555, 0x96a, 0x244, 0x60b, 0x8d3, 0x1de6, 0x831, 0x2a75, 0x4d7, 0x2616, 0x1485, 0xf16, 0x264a, 0x2bb, 0x609, 0x19d, 0x21da, 0x6d7, 0x234f, 0x2cc4, 0xaf9, 0x20c2, 0xcdd, 0x2f1, 0x1dfd, 0x1c7, 0x247b, 0xec9, 0x1978, 0x770, 0x72b, 0x1ca3, 0xe43, 0x1820, 0xdf9, 0x690, 0x926, 0x3cc, 0x2f20, 0xa7c, 0x121, 0x2f02, 0xee6, 0x2ae2, 0xa85, 0xe29, 0xd2b, 0x1326, 0x2e3d, 0x1553, 0x2ff5, 0x133, 0x2d81, 0x143d, 0x19fc, 0x174a, 0x19b9, 0x2a40, 0x22ab, 0x1d27, 0x8cf, 0x1730, 0x1386, 0x491, 0x212b, 0x2954, 0xf53, 0xbfd, 0x113a, 0x144f, 0x21f8, 0x1b0a, 0x385, 0x2ce6, 0xf63, 0x1a64, 0x48f, 0x2059, 0x1e4b, 0x1d12, 0x1f7f, 0x2255, 0x24f2, 0x16e5, 0x1242, 0xa29, 0x1a6, 0xdd5, 0x7e9, 0x2eac, 0x2e17, 0x8f7, 0x9ed, 0x1de0, 0x1588, 0x2935, 0x1c3e, 0x2534, 0xaf2, 0x2002, 0x7b4, 0x2bf, 0x1d25, 0x2273, 0x1240, 0x176e, 0x29b1, 0x217c, 0x1f5d, 0xa7d, 0x6e8, 0x1f55, 0x104e, 0xb07, 0x241e, 0xc14, 0x618, 0x1fad, 0x2cac, 0x93d, 0x1e4f, 0x2907, 0x281, 0x1bf3, 0x588, 0x277d, 0x1e6b, 0x9df, 0x629, 0x1f46, 0x19a7, 0x3c8, 0x1804, 0x1981, 0x2536, 0x19, 0x6c, 0x1092, 0x1980, 0x13ae, 0xfe4, 0x2f42, 0x9e, 0x2837, 0xea, 0x23e7, 0x73f, 0xaa3, 0x226e, 0x3c1, 0x1f94, 0x2832, 0x1408, 0xd63, 0x1559, 0x19e7, 0x273, 0x2fe5, 0x1e40, 0xa2b, 0xd34, 0x1be2, 0x353, 0x1ef7, 0x147, 0x10e3, 0xd6d, 0x248e, 0xbfc, 0xc04, 0x9aa, 0xc8, 0x360, 0x2262, 0x100b, 0x99a, 0x278f, 0x2efc, 0x1c3d, 0x29a2, 0x21ec, 0x251e, 0x1bdb, 0x2b6d, 0x2d82, 0x15f8, 0x2924, 0x2393, 0x1fd, 0x109a, 0x17b7, 0x2559, 0x20b1, 0x2147, 0xd30, 0xea6, 0xf47, 0x12c3, 0x253, 0x288c, 0xbf3, 0x22a3, 0x78a, 0x2725, 0x20d, 0x16d2, 0x47f, 0xfc, 0xfc6, 0xb7f, 0x957, 0x2514, 0x1216, 0xbda, 0x709, 0x2809, 0x172e, 0x1e60, 0x28f9, 0x23df, 0x908, 0x2445, 0x1646, 0xe38, 0x3d2, 0x160b, 0x6e6, 0x1788, 0x2fe4, 0x15d8, 0x47, 0xce8, 0x1ecb, 0x6b7, 0x2a73, 0x1619, 0x27c7, 0x633, 0x2fe7, 0x2a9a, 0x1a96, 0x297d, 0xc2d, 0x2488, 0x1953, 0xb89, 0x131c, 0x1729, 0x1b16, 0x1275, 0x1fbb, 0x184c, 0x1c28, 0x198a, 0x2934, 0x1f9, 0x2553, 0x11e5, 0xfdc, 0x2a4d, 0xdc4, 0x1146, 0x956, 0x92d, 0x21e1, 0x1a95, 0x2fa1, 0x998, 0x1c01, 0x131d, 0x2a3f, 0xb4b, 0x2cf2, 0x2fe1, 0x724, 0x1956, 0x1cce, 0x254a, 0x2a0a, 0x1497, 0x11e7, 0xc71, 0xf58, 0x77d, 0x2245, 0x40f, 0x22c, 0x871, 0x3d3, 0x18dd, 0x1cd, 0x2df0, 0x1009, 0x1a94, 0x29da, 0x1963, 0x7e7, 0x2908, 0x848, 0xc28, 0x19a2, 0x31d, 0x2c2c, 0x2608, 0x23a5, 0x542, 0x2fad, 0x865, 0x1e81, 0x2da9, 0x25e1, 0x1303, 0x240c, 0x7ba, 0x2a8, 0xc0d, 0xda8, 0x124d, 0x28a8, 0x1ff7, 0x2829, 0x146, 0xb43, 0x23ea, 0x1894, 0x2e27, 0x2dc4, 0x2d43, 0x18a3, 0x1a44, 0xbb3, 0x28b9, 0x1fe9, 0x226b, 0x1409, 0xb7a, 0x1c75, 0x4e, 0x1299, 0x1040, 0x1fcc, 0x171e, 0xb8a, 0xd1, 0x75e, 0x26ae, 0x229b, 0xec0, 0x157a, 0x111c, 0x6b5, 0x6d, 0x5ae, 0x1467, 0x1c9d, 0x200a, 0x5eb, 0x1339, 0xbff, 0x120, 0x1fbe, 0x13ff, 0x3d1, 0x2a60, 0x1b87, 0x196a, 0x57, 0x1b4f, 0x1220, 0x1d30, 0xccd, 0x248b, 0x2aa8, 0x1db7, 0x18ae, 0x10aa, 0x1425, 0x2f2c, 0x1187, 0x3a1, 0x26b8, 0x2466, 0x14e9, 0x1518, 0x2b1f, 0x1ae6, 0x238e, 0x1a78, 0x1819, 0x2284, 0x1475, 0xaf, 0x2f4, 0x13fc, 0x227d, 0x29c0, 0xf3a, 0x187a, 0x5e4, 0x1950, 0x2a25, 0x29e1, 0xddd, 0x295d, 0x1351, 0x304, 0x2bc0, 0xd2, 0xd25, 0x2195, 0x1fc9, 0x1ee6, 0x2f13, 0x6db, 0xa6a, 0x1d99, 0x2b60, 0x1234, 0x283c, 0x2ac2, 0x11a9, 0x639, 0x2290, 0x2bda, 0x32f, 0x2a5f, 0x15c0, 0x139c, 0x7e8, 0x88a, 0x43f, 0x2762, 0x1271, 0x119d, 0x1fed, 0x1b4d, 0x692, 0x1d2b, 0x1feb, 0x1380, 0x2628, 0x2a93, 0x2226, 0xe71, 0x2d1b, 0x20ab, 0x17ff, 0x1e27, 0x2fb1, 0xe65, 0x17c8, 0x1fa6, 0x43b, 0x548, 0x2256, 0x9a5, 0x71a, 0x26ea, 0x2d38, 0x1b40, 0x1b79, 0x658, 0x15a5, 0x224f, 0x248, 0xeee, 0x2f37, 0x1c30, 0x15ec, 0x1ca7, 0x255f, 0x2801, 0x18f7, 0x1727, 0xf88, 0x2b1, 0x2c45, 0x164b, 0x289f, 0x14dd, 0x2649, 0x27a3, 0x9f0, 0x21ca, 0x1f5, 0x1dd6, 0xbc3, 0x71f, 0x133e, 0x13bb, 0x2afe, 0xc35, 0x4bb, 0x2d31, 0x10a7, 0x2a04, 0x180e, 0x2613, 0x330, 0xe76, 0x19fd, 0xfe9, 0x935, 0x79, 0x1b01, 0x73c, 0x2ac6, 0x21ce, 0x1911, 0x761, 0x1084, 0x1983, 0xc3, 0x15eb, 0xe0a, 0xdd, 0x1cb1, 0xb21, 0x2a51, 0x217f, 0xb1, 0x1328, 0x9ca, 0x1d96, 0x1a0b, 0xe1b, 0x1c4b, 0x3b, 0x4d6, 0x2344, 0x199e, 0x28af, 0x1624, 0x4ae, 0x8b2, 0x2991, 0x1fb7, 0x41, 0x2780, 0x1d8b, 0xa7f, 0x110, 0x2350, 0x18aa, 0x2b2f, 0x1805, 0x1ff, 0xf0, 0x2a74, 0xe42, 0xd97, 0x85b, 0x14bc, 0x2901, 0xfd8, 0x1ab3, 0x1cef, 0xfbd, 0x2b07, 0x174f, 0x69b, 0x10c3, 0x1491, 0xde3, 0x28ca, 0x252e, 0x1849, 0x1ec2, 0x1f1b, 0x2853, 0x12ab, 0x2674, 0x238c, 0x350, 0x2ca, 0xa7, 0x4bd, 0xcc3, 0x90c, 0x892, 0x276, 0x1e55, 0x196d, 0x1194, 0x1bef, 0x66a, 0x1da1, 0x260f, 0x1c15, 0x49f, 0x120b, 0x2671, 0x1237, 0x2e0d, 0x2791, 0x17d8, 0x1e0a, 0x2a99, 0x14cf, 0xfb1, 0x15b4, 0x1462, 0x2fbb, 0xeff, 0x16b, 0x2d6a, 0x9ef, 0x5e3, 0x11c0, 0x2e76, 0x1623, 0x2db8, 0x1c88, 0x740, 0x11e1, 0x12a3, 0x977, 0x1110, 0x2163, 0x2dee, 0x47b, 0x2aa5, 0x2a22, 0x1231, 0x16e7, 0x1626, 0x12e0, 0x1d28, 0xe96, 0xb62, 0x21d0, 0xf09, 0xb30, 0xcb8, 0x2981, 0x2648, 0x155d, 0x27ee, 0xb34, 0x169, 0x1574, 0x1fe6, 0x25f4, 0x151d, 0x1801, 0x1f13, 0x1308, 0x2929, 0x6eb, 0x25e, 0x2cca, 0x1e3e, 0x248f}

func round(a int32, b int32) int32 {
	return (a + b/2) / b
}

/* constant time absolute vaue */
func nabs(x int32) int32 {
	mask := (x >> 31)
	return (x + mask) ^ mask
}

/* Montgomery stuff */

func redc(T uint64) int32 {
	m := (uint32(T) * NHS_ND)
	return int32((uint64(m)*uint64(NHS_PRIME) + T) >> NHS_WL)
}

func nres(x int32) int32 {
	return redc(uint64(x) * NHS_R2MODP)
}

func modmul(a int32, b int32) int32 {
	return redc(uint64(a) * uint64(b))
}

/* NTT code */
/* Cooley-Tukey NTT */

func ntt(x []int32) {
	t := NHS_DEGREE / 2
	q := NHS_PRIME

	/* Convert to Montgomery form */
	for j := 0; j < NHS_DEGREE; j++ {
		x[j] = nres(x[j])
	}
	m := 1
	for m < NHS_DEGREE {
		k := 0
		for i := 0; i < m; i++ {
			S := NHS_roots[m+i]
			for j := k; j < k+t; j++ {
				U := x[j]
				V := modmul(x[j+t], S)
				x[j] = U + V
				x[j+t] = U + 2*q - V
			}
			k += 2 * t
		}
		t /= 2
		m *= 2
	}
}

/* Gentleman-Sande INTT */

func intt(x []int32) {
	t := 1
	q := NHS_PRIME

	m := NHS_DEGREE / 2
	for m > 1 {
		k := 0
		for i := 0; i < m; i++ {
			S := NHS_iroots[m+i]
			for j := k; j < k+t; j++ {
				U := x[j]
				V := x[j+t]
				x[j] = U + V
				W := U + int32(NHS_DEGREE)*q - V
				x[j+t] = modmul(W, S)
			}
			k += 2 * t
		}
		t *= 2
		m /= 2
	}

	/* Last iteration merged with n^-1 */

	t = NHS_DEGREE / 2
	for j := 0; j < t; j++ {
		U := x[j]
		V := x[j+t]
		W := U + int32(NHS_DEGREE)*q - V
		x[j+t] = modmul(W, NHS_invpr)
		x[j] = modmul(U+V, NHS_inv)
	}
	/* convert back from Montgomery to "normal" form */
	for j := 0; j < NHS_DEGREE; j++ {
		x[j] = redc(uint64(x[j]))
		x[j] -= q
		x[j] += (x[j] >> (NHS_WL - 1)) & q
	}
}

/* See https://eprint.iacr.org/2016/1157.pdf */

func encode(key []byte, poly []int32) {

	q2 := NHS_PRIME / 2
	j := 0
	for i := 0; i < 256; {
		kj := key[j]
		j++
		for k := 0; k < 8; k++ {
			b := int32(kj & 1)
			poly[i] = b * q2
			poly[i+256] = b * q2
			poly[i+512] = b * q2
			poly[i+768] = b * q2
			kj >>= 1
			i++
		}
	}
}

func decode(poly []int32, key []byte) {
	q2 := NHS_PRIME / 2
	for i := 0; i < 32; i++ {
		key[i] = 0
	}

	j := 0
	for i := 0; i < 256; {
		for k := 0; k < 8; k++ {
			t := nabs(poly[i]-q2) + nabs(poly[i+256]-q2) + nabs(poly[i+512]-q2) + nabs(poly[i+768]-q2)
			b := t - NHS_PRIME
			b = (b >> 31) & 1
			key[j] = (key[j] >> 1) + byte(b<<7)
			i++
		}
		j++
	}
}

/* convert 32-byte seed to random polynomial */

func parse(seed []byte, poly []int32) {
	var hash [4 * NHS_DEGREE]byte
	sh := NewSHA3(SHA3_SHAKE128)

	for i := 0; i < 32; i++ {
		sh.Process(seed[i])
	}
	sh.Shake(hash[:], 4*NHS_DEGREE)

	j := 0
	for i := 0; i < NHS_DEGREE; i++ {
		n := int32(hash[j] & 0x7f)
		n <<= 8
		n += int32(hash[j+1])
		n <<= 8
		n += int32(hash[j+2])
		n <<= 8
		n += int32(hash[j+3])
		j += 4
		poly[i] = nres(n)
		//poly[i]=modmul(n,NHS_ONE) // reduce 31-bit random number mod q
	}
}

/* Compress 14 bits polynomial coefficients into byte array */
/* 7 bytes is 3x14 */

func nhs_pack(poly []int32, array []byte) {
	j := 0
	for i := 0; i < NHS_DEGREE; {
		a := poly[i]
		b := poly[i+1]
		c := poly[i+2]
		d := poly[i+3]
		i += 4
		array[j] = byte(a & 0xff)
		array[j+1] = byte(((a >> 8) | (b << 6)) & 0xff)
		array[j+2] = byte((b >> 2) & 0xff)
		array[j+3] = byte(((b >> 10) | (c << 4)) & 0xff)
		array[j+4] = byte((c >> 4) & 0xff)
		array[j+5] = byte(((c >> 12) | (d << 2)) & 0xff)
		array[j+6] = byte(d >> 6)
		j += 7
	}
}

func nhs_unpack(array []byte, poly []int32) {
	j := 0
	for i := 0; i < NHS_DEGREE; {
		a := int32((array[j]) & 0xff)
		b := int32((array[j+1]) & 0xff)
		c := int32((array[j+2]) & 0xff)
		d := int32((array[j+3]) & 0xff)
		e := int32((array[j+4]) & 0xff)
		f := int32((array[j+5]) & 0xff)
		g := int32((array[j+6]) & 0xff)
		j += 7
		poly[i] = a | ((b & 0x3f) << 8)
		poly[i+1] = (b >> 6) | (c << 2) | ((d & 0xf) << 10)
		poly[i+2] = (d >> 4) | (e << 4) | ((f & 3) << 12)
		poly[i+3] = (f >> 2) | (g << 6)
		i += 4
	}
}

/* See https://eprint.iacr.org/2016/1157.pdf */

func compress(poly []int32, array []byte) {

	var col int32 = 0
	j := 0
	for i := 0; i < NHS_DEGREE; {
		for k := 0; k < 8; k++ {
			b := round((poly[i]*8), NHS_PRIME) & 7
			col = (col << 3) + b
			i += 1
		}
		array[j] = byte(col & 0xff)
		array[j+1] = byte((col >> 8) & 0xff)
		array[j+2] = byte((col >> 16) & 0xff)
		j += 3
		col = 0
	}
}

func decompress(array []byte, poly []int32) {
	var col int32 = 0
	j := 0
	for i := 0; i < NHS_DEGREE; {
		col = int32(array[j+2]) & 0xff
		col = (col << 8) + (int32(array[j+1]) & 0xff)
		col = (col << 8) + (int32(array[j]) & 0xff)
		j += 3
		for k := 0; k < 8; k++ {
			b := (col & 0xe00000) >> 21
			col <<= 3
			poly[i] = round((b * NHS_PRIME), 8)
			i += 1
		}
	}
}

/* generate centered binomial distribution */

func error(rng *RAND, poly []int32) {
	var r int32
	for i := 0; i < NHS_DEGREE; i++ {
		n1 := (int32(rng.GetByte()) & 0xff) + ((int32(rng.GetByte()) & 0xff) << 8)
		n2 := (int32(rng.GetByte()) & 0xff) + ((int32(rng.GetByte()) & 0xff) << 8)
		r = 0
		for j := 0; j < 16; j++ {
			r += (n1 & 1) - (n2 & 1)
			n1 >>= 1
			n2 >>= 1
		}
		poly[i] = (r + NHS_PRIME)
	}
}

func redc_it(p []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		p[i] = redc(uint64(p[i]))
	}
}

func nres_it(p []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		p[i] = nres(p[i])
	}
}

func poly_mul(p1 []int32, p2 []int32, p3 []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		p1[i] = modmul(p2[i], p3[i])
	}
}

func poly_add(p1 []int32, p2 []int32, p3 []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		p1[i] = (p2[i] + p3[i])
	}
}

func poly_sub(p1 []int32, p2 []int32, p3 []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		p1[i] = (p2[i] + NHS_PRIME - p3[i])
	}
}

/* reduces inputs < 2q */
func poly_soft_reduce(poly []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		e := poly[i] - NHS_PRIME
		poly[i] = e + ((e >> (NHS_WL - 1)) & NHS_PRIME)
	}
}

/* fully reduces modulo q */
func poly_hard_reduce(poly []int32) {
	for i := 0; i < NHS_DEGREE; i++ {
		e := modmul(poly[i], NHS_ONE)
		e = e - NHS_PRIME
		poly[i] = e + ((e >> (NHS_WL - 1)) & NHS_PRIME)
	}
}

/* API files */

func NHS_SERVER_1(rng *RAND, SB []byte, S []byte) {
	var seed [32]byte
	var array [1792]byte
	var s [NHS_DEGREE]int32
	var e [NHS_DEGREE]int32
	var b [NHS_DEGREE]int32

	for i := 0; i < 32; i++ {
		seed[i] = rng.GetByte()
	}

	parse(seed[:], b[:])

	error(rng, e[:])
	error(rng, s[:])

	ntt(s[:])
	ntt(e[:])
	poly_mul(b[:], b[:], s[:])
	poly_add(b[:], b[:], e[:])
	poly_hard_reduce(b[:])

	redc_it(b[:])
	nhs_pack(b[:], array[:])

	for i := 0; i < 32; i++ {
		SB[i] = seed[i]
	}

	for i := 0; i < 1792; i++ {
		SB[i+32] = array[i]
	}

	poly_hard_reduce(s[:])
	nhs_pack(s[:], array[:])

	for i := 0; i < 1792; i++ {
		S[i] = array[i]
	}

}

func NHS_CLIENT(rng *RAND, SB []byte, UC []byte, KEY []byte) {
	sh := NewSHA3(SHA3_HASH256)
	var seed [32]byte
	var array [1792]byte
	var key [32]byte
	var cc [384]byte

	var sd [NHS_DEGREE]int32
	var ed [NHS_DEGREE]int32
	var u [NHS_DEGREE]int32
	var k [NHS_DEGREE]int32
	var c [NHS_DEGREE]int32

	error(rng, sd[:])
	error(rng, ed[:])

	ntt(sd[:])
	ntt(ed[:])

	for i := 0; i < 32; i++ {
		seed[i] = SB[i]
	}

	for i := 0; i < 1792; i++ {
		array[i] = SB[i+32]
	}

	parse(seed[:], u[:])

	poly_mul(u[:], u[:], sd[:])
	poly_add(u[:], u[:], ed[:])
	poly_hard_reduce(u[:])

	for i := 0; i < 32; i++ {
		key[i] = rng.GetByte()
	}

	for i := 0; i < 32; i++ {
		sh.Process(key[i])
	}
	sh.Hash(key[:])

	encode(key[:], k[:])

	nhs_unpack(array[:], c[:])
	nres_it(c[:])

	poly_mul(c[:], c[:], sd[:])
	intt(c[:])
	error(rng, ed[:])
	poly_add(c[:], c[:], ed[:])
	poly_add(c[:], c[:], k[:])

	compress(c[:], cc[:])

	sh.Init(SHA3_HASH256)
	for i := 0; i < 32; i++ {
		sh.Process(key[i])
	}
	sh.Hash(key[:])

	for i := 0; i < 32; i++ {
		KEY[i] = key[i]
	}

	redc_it(u[:])
	nhs_pack(u[:], array[:])

	for i := 0; i < 1792; i++ {
		UC[i] = array[i]
	}

	for i := 0; i < 384; i++ {
		UC[i+1792] = cc[i]
	}
}

func NHS_SERVER_2(S []byte, UC []byte, KEY []byte) {
	sh := NewSHA3(SHA3_HASH256)

	var c [NHS_DEGREE]int32
	var s [NHS_DEGREE]int32
	var k [NHS_DEGREE]int32

	var array [1792]byte
	var key [32]byte
	var cc [384]byte

	for i := 0; i < 1792; i++ {
		array[i] = UC[i]
	}

	nhs_unpack(array[:], k[:])
	nres_it(k[:])

	for i := 0; i < 384; i++ {
		cc[i] = UC[i+1792]
	}

	decompress(cc[:], c[:])

	for i := 0; i < 1792; i++ {
		array[i] = S[i]
	}

	nhs_unpack(array[:], s[:])

	poly_mul(k[:], k[:], s[:])
	intt(k[:])
	poly_sub(k[:], c[:], k[:])
	poly_soft_reduce(k[:])

	decode(k[:], key[:])

	for i := 0; i < 32; i++ {
		sh.Process(key[i])
	}
	sh.Hash(key[:])

	for i := 0; i < 32; i++ {
		KEY[i] = key[i]
	}
}

/*
func main() {

	srng:=NewRAND()
	var sraw [100]byte
	for i:=0;i<100;i++ {sraw[i]=byte(i+1)}
	srng.Seed(100,sraw[:])

							crng:=NewRAND()
							var craw [100]byte
							for i:=0;i<100;i++ {craw[i]=byte(i+2)}
							crng.Seed(100,craw[:])

	var S [1792] byte

				var SB [1824] byte
	NHS_SERVER_1(srng,SB[:],S[:])
				var UC [2176] byte
							var KEYB [32] byte
							NHS_CLIENT(crng,SB[:],UC[:],KEYB[:])

							fmt.Printf("Bob's Key= ")
							for i:=0;i<32;i++ {
								fmt.Printf("%02x", KEYB[i])
							}
							fmt.Printf("\n")
	var KEYA [32] byte
	NHS_SERVER_2(S[:],UC[:],KEYA[:])

	fmt.Printf("Alice Key= ")
	for i:=0;i<32;i++ {
		fmt.Printf("%02x", KEYA[i])
	}

}
*/
