CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/ext/cityhash/city.cpp
Views: 1401
// Copyright (c) 2011 Google, Inc.1//2// Permission is hereby granted, free of charge, to any person obtaining a copy3// of this software and associated documentation files (the "Software"), to deal4// in the Software without restriction, including without limitation the rights5// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell6// copies of the Software, and to permit persons to whom the Software is7// furnished to do so, subject to the following conditions:8//9// The above copyright notice and this permission notice shall be included in10// all copies or substantial portions of the Software.11//12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR13// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,14// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE15// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER16// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,17// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN18// THE SOFTWARE.19//20// CityHash, by Geoff Pike and Jyrki Alakuijala21//22// This file provides CityHash64() and related functions.23//24// It's probably possible to create even faster hash functions by25// writing a program that systematically explores some of the space of26// possible hash functions, by using SIMD instructions, or by27// compromising on hash quality.2829#include "city.h"3031#include <algorithm>32#include <stdlib.h> // To check for glibc33#include <string.h> // for memcpy and memset34#include <cstdlib>3536using namespace std;3738static uint64 UNALIGNED_LOAD64(const char *p) {39uint64 result;40memcpy(&result, p, sizeof(result));41return result;42}4344static uint32 UNALIGNED_LOAD32(const char *p) {45uint32 result;46memcpy(&result, p, sizeof(result));47return result;48}4950#ifdef _MSC_VER51#define bswap_32(x) _byteswap_ulong(x)52#define bswap_64(x) _byteswap_uint64(x)5354#elif defined(__GLIBC__) || defined(__ANDROID__)55#include <byteswap.h>5657#elif defined(__APPLE__)58// Mac OS X / Darwin features59#include <libkern/OSByteOrder.h>60#define bswap_32(x) OSSwapInt32(x)61#define bswap_64(x) OSSwapInt64(x)6263#elif defined(__sun) || defined(sun)64#include <sys/byteorder.h>65#define bswap_32(x) BSWAP_32(x)66#define bswap_64(x) BSWAP_64(x)6768#elif defined(__DragonFly__) || defined(__FreeBSD__)69#include <sys/endian.h>70#define bswap_32(x) bswap32(x)71#define bswap_64(x) bswap64(x)7273#elif defined(__Bitrig__) || defined(__OpenBSD__)74#include <sys/types.h>75#define bswap_32(x) swap32(x)76#define bswap_64(x) swap64(x)7778#elif defined(__NetBSD__)79#include <sys/types.h>80#include <machine/bswap.h>81#if defined(__BSWAP_RENAME) && !defined(__bswap_32)82#define bswap_32(x) bswap32(x)83#define bswap_64(x) bswap64(x)84#endif8586#else87#define bswap_32(x) (0 | (((x) & 0x000000ff) << 24) \88| (((x) & 0x0000ff00) << 8) \89| (((x) & 0x00ff0000) >> 8) \90| (((x) & 0xff000000) >> 24))91#define bswap_64(x) (0 | (((x) & 0x00000000000000ffULL) << 56) \92| (((x) & 0x000000000000ff00ULL) << 40) \93| (((x) & 0x0000000000ff0000ULL) << 24) \94| (((x) & 0x00000000ff000000ULL) << 8) \95| (((x) & 0x000000ff00000000ULL) >> 8) \96| (((x) & 0x0000ff0000000000ULL) >> 24) \97| (((x) & 0x00ff000000000000ULL) >> 40) \98| (((x) & 0xff00000000000000ULL) >> 56))99#endif100101#ifdef WORDS_BIGENDIAN102#define uint32_in_expected_order(x) (bswap_32(x))103#define uint64_in_expected_order(x) (bswap_64(x))104#else105#define uint32_in_expected_order(x) (x)106#define uint64_in_expected_order(x) (x)107#endif108109#if !defined(LIKELY)110#if HAVE_BUILTIN_EXPECT111#define LIKELY(x) (__builtin_expect(!!(x), 1))112#else113#define LIKELY(x) (x)114#endif115#endif116117static uint64 Fetch64(const char *p) {118return uint64_in_expected_order(UNALIGNED_LOAD64(p));119}120121static uint32 Fetch32(const char *p) {122return uint32_in_expected_order(UNALIGNED_LOAD32(p));123}124125// Some primes between 2^63 and 2^64 for various uses.126static const uint64 k0 = 0xc3a5c85c97cb3127ULL;127static const uint64 k1 = 0xb492b66fbe98f273ULL;128static const uint64 k2 = 0x9ae16a3b2f90404fULL;129130// Magic numbers for 32-bit hashing. Copied from Murmur3.131static const uint32 c1 = 0xcc9e2d51;132static const uint32 c2 = 0x1b873593;133134// A 32-bit to 32-bit integer hash copied from Murmur3.135static uint32 fmix(uint32 h)136{137h ^= h >> 16;138h *= 0x85ebca6b;139h ^= h >> 13;140h *= 0xc2b2ae35;141h ^= h >> 16;142return h;143}144145static uint32 Rotate32(uint32 val, int shift) {146// Avoid shifting by 32: doing so yields an undefined result.147return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));148}149150#undef PERMUTE3151#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0)152153static uint32 Mur(uint32 a, uint32 h) {154// Helper from Murmur3 for combining two 32-bit values.155a *= c1;156a = Rotate32(a, 17);157a *= c2;158h ^= a;159h = Rotate32(h, 19);160return h * 5 + 0xe6546b64;161}162163static uint32 Hash32Len13to24(const char *s, size_t len) {164uint32 a = Fetch32(s - 4 + (len >> 1));165uint32 b = Fetch32(s + 4);166uint32 c = Fetch32(s + len - 8);167uint32 d = Fetch32(s + (len >> 1));168uint32 e = Fetch32(s);169uint32 f = Fetch32(s + len - 4);170uint32 h = (uint32)len;171172return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));173}174175static uint32 Hash32Len0to4(const char *s, size_t len) {176uint32 b = 0;177uint32 c = 9;178for (size_t i = 0; i < len; i++) {179signed char v = s[i];180b = b * c1 + v;181c ^= b;182}183return fmix(Mur(b, Mur((uint32)len, c)));184}185186static uint32 Hash32Len5to12(const char *s, size_t len) {187uint32 a = (uint32)len, b = (uint32)len * 5, c = 9, d = b;188a += Fetch32(s);189b += Fetch32(s + len - 4);190c += Fetch32(s + ((len >> 1) & 4));191return fmix(Mur(c, Mur(b, Mur(a, d))));192}193194uint32 CityHash32(const char *s, size_t len) {195if (len <= 24) {196return len <= 12 ?197(len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :198Hash32Len13to24(s, len);199}200201// len > 24202uint32 h = (uint32)len, g = c1 * (uint32)len, f = g;203uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;204uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;205uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;206uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;207uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;208h ^= a0;209h = Rotate32(h, 19);210h = h * 5 + 0xe6546b64;211h ^= a2;212h = Rotate32(h, 19);213h = h * 5 + 0xe6546b64;214g ^= a1;215g = Rotate32(g, 19);216g = g * 5 + 0xe6546b64;217g ^= a3;218g = Rotate32(g, 19);219g = g * 5 + 0xe6546b64;220f += a4;221f = Rotate32(f, 19);222f = f * 5 + 0xe6546b64;223size_t iters = (len - 1) / 20;224do {225uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2;226uint32 a1 = Fetch32(s + 4);227uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;228uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;229uint32 a4 = Fetch32(s + 16);230h ^= a0;231h = Rotate32(h, 18);232h = h * 5 + 0xe6546b64;233f += a1;234f = Rotate32(f, 19);235f = f * c1;236g += a2;237g = Rotate32(g, 18);238g = g * 5 + 0xe6546b64;239h ^= a3 + a1;240h = Rotate32(h, 19);241h = h * 5 + 0xe6546b64;242g ^= a4;243g = bswap_32(g) * 5;244h += a4 * 5;245h = bswap_32(h);246f += a0;247PERMUTE3(f, h, g);248s += 20;249} while (--iters != 0);250g = Rotate32(g, 11) * c1;251g = Rotate32(g, 17) * c1;252f = Rotate32(f, 11) * c1;253f = Rotate32(f, 17) * c1;254h = Rotate32(h + g, 19);255h = h * 5 + 0xe6546b64;256h = Rotate32(h, 17) * c1;257h = Rotate32(h + f, 19);258h = h * 5 + 0xe6546b64;259h = Rotate32(h, 17) * c1;260return h;261}262263// Bitwise right rotate. Normally this will compile to a single264// instruction, especially if the shift is a manifest constant.265static uint64 Rotate(uint64 val, int shift) {266// Avoid shifting by 64: doing so yields an undefined result.267return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));268}269270static uint64 ShiftMix(uint64 val) {271return val ^ (val >> 47);272}273274static uint64 HashLen16(uint64 u, uint64 v) {275return Hash128to64(uint128(u, v));276}277278static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {279// Murmur-inspired hashing.280uint64 a = (u ^ v) * mul;281a ^= (a >> 47);282uint64 b = (v ^ a) * mul;283b ^= (b >> 47);284b *= mul;285return b;286}287288static uint64 HashLen0to16(const char *s, size_t len) {289if (len >= 8) {290uint64 mul = k2 + len * 2;291uint64 a = Fetch64(s) + k2;292uint64 b = Fetch64(s + len - 8);293uint64 c = Rotate(b, 37) * mul + a;294uint64 d = (Rotate(a, 25) + b) * mul;295return HashLen16(c, d, mul);296}297if (len >= 4) {298uint64 mul = k2 + len * 2;299uint64 a = Fetch32(s);300return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);301}302if (len > 0) {303uint8 a = s[0];304uint8 b = s[len >> 1];305uint8 c = s[len - 1];306uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);307uint32 z = (uint32)len + (static_cast<uint32>(c) << 2);308return ShiftMix(y * k2 ^ z * k0) * k2;309}310return k2;311}312313// This probably works well for 16-byte strings as well, but it may be overkill314// in that case.315static uint64 HashLen17to32(const char *s, size_t len) {316uint64 mul = k2 + len * 2;317uint64 a = Fetch64(s) * k1;318uint64 b = Fetch64(s + 8);319uint64 c = Fetch64(s + len - 8) * mul;320uint64 d = Fetch64(s + len - 16) * k2;321return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,322a + Rotate(b + k2, 18) + c, mul);323}324325// Return a 16-byte hash for 48 bytes. Quick and dirty.326// Callers do best to use "random-looking" values for a and b.327static pair<uint64, uint64> WeakHashLen32WithSeeds(328uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) {329a += w;330b = Rotate(b + a + z, 21);331uint64 c = a;332a += x;333a += y;334b += Rotate(a, 44);335return make_pair(a + z, b + c);336}337338// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.339static pair<uint64, uint64> WeakHashLen32WithSeeds(340const char* s, uint64 a, uint64 b) {341return WeakHashLen32WithSeeds(Fetch64(s),342Fetch64(s + 8),343Fetch64(s + 16),344Fetch64(s + 24),345a,346b);347}348349// Return an 8-byte hash for 33 to 64 bytes.350static uint64 HashLen33to64(const char *s, size_t len) {351uint64 mul = k2 + len * 2;352uint64 a = Fetch64(s) * k2;353uint64 b = Fetch64(s + 8);354uint64 c = Fetch64(s + len - 24);355uint64 d = Fetch64(s + len - 32);356uint64 e = Fetch64(s + 16) * k2;357uint64 f = Fetch64(s + 24) * 9;358uint64 g = Fetch64(s + len - 8);359uint64 h = Fetch64(s + len - 16) * mul;360uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;361uint64 v = ((a + g) ^ d) + f + 1;362uint64 w = bswap_64((u + v) * mul) + h;363uint64 x = Rotate(e + f, 42) + c;364uint64 y = (bswap_64((v + w) * mul) + g) * mul;365uint64 z = e + f + c;366a = bswap_64((x + z) * mul + y) + b;367b = ShiftMix((z + a) * mul + d + h) * mul;368return b + x;369}370371uint64 CityHash64(const char *s, size_t len) {372if (len <= 32) {373if (len <= 16) {374return HashLen0to16(s, len);375} else {376return HashLen17to32(s, len);377}378} else if (len <= 64) {379return HashLen33to64(s, len);380}381382// For strings over 64 bytes we hash the end first, and then as we383// loop we keep 56 bytes of state: v, w, x, y, and z.384uint64 x = Fetch64(s + len - 40);385uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);386uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));387pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);388pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);389x = x * k1 + Fetch64(s);390391// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.392len = (len - 1) & ~static_cast<size_t>(63);393do {394x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;395y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;396x ^= w.second;397y += v.first + Fetch64(s + 40);398z = Rotate(z + w.first, 33) * k1;399v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);400w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));401std::swap(z, x);402s += 64;403len -= 64;404} while (len != 0);405return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,406HashLen16(v.second, w.second) + x);407}408409uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) {410return CityHash64WithSeeds(s, len, k2, seed);411}412413uint64 CityHash64WithSeeds(const char *s, size_t len,414uint64 seed0, uint64 seed1) {415return HashLen16(CityHash64(s, len) - seed0, seed1);416}417418// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings419// of any length representable in signed long. Based on City and Murmur.420static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {421uint64 a = Uint128Low64(seed);422uint64 b = Uint128High64(seed);423uint64 c = 0;424uint64 d = 0;425signed long l = (signed long)len - 16;426if (l <= 0) { // len <= 16427a = ShiftMix(a * k1) * k1;428c = b * k1 + HashLen0to16(s, len);429d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));430} else { // len > 16431c = HashLen16(Fetch64(s + len - 8) + k1, a);432d = HashLen16(b + len, c + Fetch64(s + len - 16));433a += d;434do {435a ^= ShiftMix(Fetch64(s) * k1) * k1;436a *= k1;437b ^= a;438c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;439c *= k1;440d ^= c;441s += 16;442l -= 16;443} while (l > 0);444}445a = HashLen16(a, c);446b = HashLen16(d, b);447return uint128(a ^ b, HashLen16(b, a));448}449450uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {451if (len < 128) {452return CityMurmur(s, len, seed);453}454455// We expect len >= 128 to be the common case. Keep 56 bytes of state:456// v, w, x, y, and z.457pair<uint64, uint64> v, w;458uint64 x = Uint128Low64(seed);459uint64 y = Uint128High64(seed);460uint64 z = len * k1;461v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);462v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);463w.first = Rotate(y + z, 35) * k1 + x;464w.second = Rotate(x + Fetch64(s + 88), 53) * k1;465466// This is the same inner loop as CityHash64(), manually unrolled.467do {468x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;469y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;470x ^= w.second;471y += v.first + Fetch64(s + 40);472z = Rotate(z + w.first, 33) * k1;473v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);474w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));475std::swap(z, x);476s += 64;477x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;478y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;479x ^= w.second;480y += v.first + Fetch64(s + 40);481z = Rotate(z + w.first, 33) * k1;482v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);483w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));484std::swap(z, x);485s += 64;486len -= 128;487} while (LIKELY(len >= 128));488x += Rotate(v.first + z, 49) * k0;489y = y * k0 + Rotate(w.second, 37);490z = z * k0 + Rotate(w.first, 27);491w.first *= 9;492v.first *= k0;493// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.494for (size_t tail_done = 0; tail_done < len; ) {495tail_done += 32;496y = Rotate(x + y, 42) * k0 + v.second;497w.first += Fetch64(s + len - tail_done + 16);498x = x * k0 + w.first;499z += w.second + Fetch64(s + len - tail_done);500w.second += v.first;501v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);502v.first *= k0;503}504// At this point our 56 bytes of state should contain more than505// enough information for a strong 128-bit hash. We use two506// different 56-byte-to-8-byte hashes to get a 16-byte final result.507x = HashLen16(x, v.first);508y = HashLen16(y + z, w.first);509return uint128(HashLen16(x + v.second, w.second) + y,510HashLen16(x + w.second, y + v.second));511}512513uint128 CityHash128(const char *s, size_t len) {514return len >= 16 ?515CityHash128WithSeed(s + 16, len - 16,516uint128(Fetch64(s), Fetch64(s + 8) + k0)) :517CityHash128WithSeed(s, len, uint128(k0, k1));518}519520#if defined(__x86_64__) && defined(__SSE4_2__)521#include "citycrc.h"522#include <nmmintrin.h>523524// Requires len >= 240.525static void CityHashCrc256Long(const char *s, size_t len,526uint32 seed, uint64 *result) {527uint64 a = Fetch64(s + 56) + k0;528uint64 b = Fetch64(s + 96) + k0;529uint64 c = result[0] = HashLen16(b, len);530uint64 d = result[1] = Fetch64(s + 120) * k0 + len;531uint64 e = Fetch64(s + 184) + seed;532uint64 f = 0;533uint64 g = 0;534uint64 h = c + d;535uint64 x = seed;536uint64 y = 0;537uint64 z = 0;538539// 240 bytes of input per iter.540size_t iters = len / 240;541len -= iters * 240;542do {543#undef CHUNK544#define CHUNK(r) \545PERMUTE3(x, z, y); \546b += Fetch64(s); \547c += Fetch64(s + 8); \548d += Fetch64(s + 16); \549e += Fetch64(s + 24); \550f += Fetch64(s + 32); \551a += b; \552h += f; \553b += c; \554f += d; \555g += e; \556e += z; \557g += x; \558z = _mm_crc32_u64(z, b + g); \559y = _mm_crc32_u64(y, e + h); \560x = _mm_crc32_u64(x, f + a); \561e = Rotate(e, r); \562c += e; \563s += 40564565CHUNK(0); PERMUTE3(a, h, c);566CHUNK(33); PERMUTE3(a, h, f);567CHUNK(0); PERMUTE3(b, h, f);568CHUNK(42); PERMUTE3(b, h, d);569CHUNK(0); PERMUTE3(b, h, e);570CHUNK(33); PERMUTE3(a, h, e);571} while (--iters > 0);572573while (len >= 40) {574CHUNK(29);575e ^= Rotate(a, 20);576h += Rotate(b, 30);577g ^= Rotate(c, 40);578f += Rotate(d, 34);579PERMUTE3(c, h, g);580len -= 40;581}582if (len > 0) {583s = s + len - 40;584CHUNK(33);585e ^= Rotate(a, 43);586h += Rotate(b, 42);587g ^= Rotate(c, 41);588f += Rotate(d, 40);589}590result[0] ^= h;591result[1] ^= g;592g += h;593a = HashLen16(a, g + z);594x += y << 32;595b += x;596c = HashLen16(c, z) + h;597d = HashLen16(d, e + result[0]);598g += e;599h += HashLen16(x, f);600e = HashLen16(a, d) + g;601z = HashLen16(b, c) + a;602y = HashLen16(g, h) + c;603result[0] = e + z + y + x;604a = ShiftMix((a + y) * k0) * k0 + b;605result[1] += a + result[0];606a = ShiftMix(a * k0) * k0 + c;607result[2] = a + result[1];608a = ShiftMix((a + e) * k0) * k0;609result[3] = a + result[2];610}611612// Requires len < 240.613static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) {614char buf[240];615memcpy(buf, s, len);616memset(buf + len, 0, 240 - len);617CityHashCrc256Long(buf, 240, ~static_cast<uint32>(len), result);618}619620void CityHashCrc256(const char *s, size_t len, uint64 *result) {621if (LIKELY(len >= 240)) {622CityHashCrc256Long(s, len, 0, result);623} else {624CityHashCrc256Short(s, len, result);625}626}627628uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {629if (len <= 900) {630return CityHash128WithSeed(s, len, seed);631} else {632uint64 result[4];633CityHashCrc256(s, len, result);634uint64 u = Uint128High64(seed) + result[0];635uint64 v = Uint128Low64(seed) + result[1];636return uint128(HashLen16(u, v + result[2]),637HashLen16(Rotate(v, 32), u * k0 + result[3]));638}639}640641uint128 CityHashCrc128(const char *s, size_t len) {642if (len <= 900) {643return CityHash128(s, len);644} else {645uint64 result[4];646CityHashCrc256(s, len, result);647return uint128(result[2], result[3]);648}649}650651#endif652653654