/*
 * 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.
 */
/*
 * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40
 * Copyright (c) 2016, The Gocql authors,
 * provided under the BSD-3-Clause License.
 * See the NOTICE file distributed with this work for additional information.
 */

package murmur

import (
	"encoding/hex"
	"fmt"
	"strconv"
	"testing"
)

func TestRotl(t *testing.T) {
	tests := []struct {
		in, rotate, exp int64
	}{
		{123456789, 33, 1060485742448345088},
		{-123456789, 33, -1060485733858410497},
		{-12345678987654, 33, 1756681988166642059},

		{7210216203459776512, 31, -4287945813905642825},
		{2453826951392495049, 27, -2013042863942636044},
		{270400184080946339, 33, -3553153987756601583},
		{2060965185473694757, 31, 6290866853133484661},
		{3075794793055692309, 33, -3158909918919076318},
		{-6486402271863858009, 31, 405973038345868736},
	}

	for _, test := range tests {
		t.Run(fmt.Sprintf("%d >> %d", test.in, test.rotate), func(t *testing.T) {
			if v := rotl(test.in, uint8(test.rotate)); v != test.exp {
				t.Fatalf("expected %d got %d", test.exp, v)
			}
		})
	}
}

func TestFmix(t *testing.T) {
	tests := []struct {
		in, exp int64
	}{
		{123456789, -8107560010088384378},
		{-123456789, -5252787026298255965},
		{-12345678987654, -1122383578793231303},
		{-1241537367799374202, 3388197556095096266},
		{-7566534940689533355, 4729783097411765989},
	}

	for _, test := range tests {
		t.Run(strconv.Itoa(int(test.in)), func(t *testing.T) {
			if v := fmix(test.in); v != test.exp {
				t.Fatalf("expected %d got %d", test.exp, v)
			}
		})
	}

}

func TestMurmur3H1_CassandraSign(t *testing.T) {
	key, err := hex.DecodeString("00104327529fb645dd00b883ec39ae448bb800000400066a6b00")
	if err != nil {
		t.Fatal(err)
	}
	h := Murmur3H1(key)
	const exp int64 = -9223371632693506265

	if h != exp {
		t.Fatalf("expected %d got %d", exp, h)
	}
}

// Test the implementation of murmur3
func TestMurmur3H1(t *testing.T) {
	// these examples are based on adding a index number to a sample string in
	// a loop. The expected values were generated by the java datastax murmur3
	// implementation. The number of examples here of increasing lengths ensure
	// test coverage of all tail-length branches in the murmur3 algorithm
	seriesExpected := [...]uint64{
		0x0000000000000000, // ""
		0x2ac9debed546a380, // "0"
		0x649e4eaa7fc1708e, // "01"
		0xce68f60d7c353bdb, // "012"
		0x0f95757ce7f38254, // "0123"
		0x0f04e459497f3fc1, // "01234"
		0x88c0a92586be0a27, // "012345"
		0x13eb9fb82606f7a6, // "0123456"
		0x8236039b7387354d, // "01234567"
		0x4c1e87519fe738ba, // "012345678"
		0x3f9652ac3effeb24, // "0123456789"
		0x3f33760ded9006c6, // "01234567890"
		0xaed70a6631854cb1, // "012345678901"
		0x8a299a8f8e0e2da7, // "0123456789012"
		0x624b675c779249a6, // "01234567890123"
		0xa4b203bb1d90b9a3, // "012345678901234"
		0xa3293ad698ecb99a, // "0123456789012345"
		0xbc740023dbd50048, // "01234567890123456"
		0x3fe5ab9837d25cdd, // "012345678901234567"
		0x2d0338c1ca87d132, // "0123456789012345678"
	}
	sample := ""
	for i, expected := range seriesExpected {
		assertMurmur3H1(t, []byte(sample), expected)

		sample = sample + strconv.Itoa(i%10)
	}

	// Here are some test examples from other driver implementations
	assertMurmur3H1(t, []byte("hello"), 0xcbd8a7b341bd9b02)
	assertMurmur3H1(t, []byte("hello, world"), 0x342fac623a5ebc8e)
	assertMurmur3H1(t, []byte("19 Jan 2038 at 3:14:07 AM"), 0xb89e5988b737affc)
	assertMurmur3H1(t, []byte("The quick brown fox jumps over the lazy dog."), 0xcd99481f9ee902c9)
}

// helper function for testing the murmur3 implementation
func assertMurmur3H1(t *testing.T, data []byte, expected uint64) {
	actual := Murmur3H1(data)
	if actual != int64(expected) {
		t.Errorf("Expected h1 = %x for data = %x, but was %x", int64(expected), data, actual)
	}
}

// Benchmark of the performance of the murmur3 implementation
func BenchmarkMurmur3H1(b *testing.B) {
	data := make([]byte, 1024)
	for i := 0; i < 1024; i++ {
		data[i] = byte(i)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			h1 := Murmur3H1(data)
			if h1 != int64(7627370222079200297) {
				b.Fatalf("expected %d got %d", int64(7627370222079200297), h1)
			}
		}
	})
}
