/* vim: set ts=8 sts=4 sw=4 tw=80 noet: */
/*======================================================================
Copyright (C) 2004,2005,2009,2011 Walter Doekes <walter+tthsum@wjd.nu>
This file is part of tthsum.

tthsum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

tthsum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with tthsum.  If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "thex.h"

#include "base32.h"
#include "test.h"
#include <string.h>


static int thex_cmp(struct rofile* stream, const char* base32) {
    /* Don't forget to zero out cmpres/tstres */
    uint64_t cmpres[3] = {_ULL(0), _ULL(0), _ULL(0)};
    uint64_t tstres[3] = {_ULL(0), _ULL(0), _ULL(0)};
    char tstbase32[40];
    if (base32touint64(&cmpres[0], base32, 3) == -1)
	FAIL1("base32touint64 returned -1 on %s", base32);
    if (thex_tiger_root(stream, &tstres[0], NULL) == -1)
	FAIL1("thex_tiger_root returned -1 on %s", base32);
    if (uint64tobase32(tstbase32, tstres, 3) == -1)
	FAIL1("uint64tobase32 returned -1 on %s", base32);
    if (memcmp(&cmpres[0], &tstres[0], 3 * sizeof(uint64_t)) != 0) {
	unsigned i;
	char hexbuf[49];
	memset(hexbuf, '.', 48);
	hexbuf[48] = '\0';
	for (i = 0; i < 3 * sizeof(uint64_t); ++i) {
	    char cmphigh = (char)(*(((uint8_t*)&cmpres[0]) + i) >> 4);
	    char cmplow = (char)(*(((uint8_t*)&cmpres[0]) + i) & 0xf);
	    char tsthigh = (char)(*(((uint8_t*)&tstres[0]) + i) >> 4);
	    char tstlow = (char)(*(((uint8_t*)&tstres[0]) + i) & 0xf);
	    if (cmphigh != tsthigh)
		hexbuf[2*i] = tsthigh >= 10 ? 'a'+(tsthigh-10) : '0'+tsthigh;
	    if (cmplow != tstlow)
		hexbuf[2*i+1] = tstlow >= 10 ? 'a'+(tstlow-10) : '0'+tstlow;
	}
	FAIL3("memcmp: mismatch on %s != %s: %s", base32, tstbase32, hexbuf);
    }
    return 0;
}

static int thex_memcmp(const char* data, unsigned length, const char* base32) {
    int ret;
    struct rofile* stream;
    stream = rofopen_mem(data, length);
    if (stream == NULL)
	FAIL1("rofopen_mem failed on %s", base32);
    ret = thex_cmp(stream, base32);
    rofclose(stream);
    return ret;
}

static int thex_strcmp(const char* str, const char* base32) {
    return thex_memcmp(str, strlen(str), base32);
}

static int test_standard_hashes() {
    char buf[1026];
    int i;
    for (i = 0; i < 1025; ++i)
	buf[i] = 'A';
    buf[1025] = '\0';

    return thex_strcmp("", "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ")
	 + thex_memcmp("\0", 1, "VK54ZIEEVTWNAUI5D5RDFIL37LX2IQNSTAXFKSA")
	 + thex_memcmp(buf, 1024, "L66Q4YVNAFWVS23X2HJIRA5ZJ7WXR3F26RSASFA")
	 + thex_memcmp(buf, 1025, "PZMRYHGY6LTBEH63ZWAHDORHSYTLO4LEFUIKHWY");
}

static int test_extra_hashes() {
#define buflen (20 * 1024 * 28) /* \x00A-Z\xFF */
    char buf[buflen];
    int i;
    for (i = 0; i < buflen; i += 28)
	memcpy(buf + i, "\x00" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "\xff", 28);
    return thex_strcmp("abcdefghijklmnopqrstuvwxyz",
	    "LMHNA2VYO465P2RDOGTR2CL6XKHZNI2X4CCUY5Y")
	+ thex_memcmp("\xff\x00\x01", 3,
	    "BRA2Y3Q5YXZKONQOTBA5BMIWTWMFVFISP3AFLUY")
	+ thex_memcmp(buf, buflen, /* 573440 */
	    "YWCGBHG5J26IDNFH3M55MR7674GYLSBXBWITCZA")
	+ thex_memcmp(buf, buflen - 5463, /* 567977 */
	    "OQMPMBCCCTSJJSKNO3FGFZX4T636THZB57UT7HI");
#undef buflen
}

static int test_pavelch_hashes() {
    char buf[256];
    int i;
    for (i = 0; i < 256; ++i)
	buf[i] = (char)i;
    return thex_memcmp(buf, 125, "B5LOOSIJN7KWAWIJUJPNHKLCZ5TGDZ34NKB7LVA")
	+ thex_memcmp(buf, 126, "UFWW3Q7PYSXGWUACF6C5F4377A4BYNUM5JENX6Y")
	+ thex_memcmp(buf, 127, "YJ6JWRX74NIKSRYWR2MVUEHF2K2RKUEHDK6MG7I")
	+ thex_memcmp(buf, 128, "ZU6NIYQCFYZKUZHOJABKACHEQV7GQMT75Q7H6MQ")
	+ thex_memcmp(buf, 129, "3YDAQRYUVS5DZNQWTJNXIQZ65PJV22VQ6HEY32I")
	/* +1 */
	+ thex_memcmp(buf + 1, 125, "CXMZBH4NNFUJ47TYUDNZFAMUUWLCHZZFHKU5IAA")
	+ thex_memcmp(buf + 1, 126, "3Y4VRHOMBRSUQZ4UHAAZI24YRCZUOUTCPHFBLPI")
	+ thex_memcmp(buf + 1, 127, "GT5HY5HOM7A7SLAL4HH5JMXUNIKP7OMZMBESL5Q")
	+ thex_memcmp(buf + 1, 128, "32RM6ILFACIWWHX4JGFYI5U5CD6CUTYFDHR5POI")
	+ thex_memcmp(buf + 1, 129, "DGD6BKKGH3AQUVA7DDOZWS2LRPYTCG5C23GNLKA")
	/* +2 */
	+ thex_memcmp(buf + 2, 125, "LGBYVEKTCQ5FYMU4XDQN2CN63J3JDFY4IABMETA")
	+ thex_memcmp(buf + 2, 126, "T7IEE2ZZJ3XI2QUSJSKOKXGC74KFT44M5FSX6WA")
	+ thex_memcmp(buf + 2, 127, "7KWOAII6AEAV67SK2ACZMN3HTN2ZYBNRSSUKGLA")
	+ thex_memcmp(buf + 2, 128, "XRTRHLCALO34QVC6NSXY4JTSC6THMGFXRUIW72I")
	+ thex_memcmp(buf + 2, 129, "O74RH63EWYNJHAFE7PXUF3JD4LQWGJOGLS4UU4Q")
	/* last */
	+ thex_memcmp(buf, 254, "V7NMKNTONQNSWUQENDMHVLPFLIXKGDDQBIWJHYA")
	+ thex_memcmp(buf, 255, "A7NQ5IZLVIPPHVDFAFHA5TDSBYJ5MKYTQMRJNBA")
	+ thex_memcmp(buf, 256, "YAJKVS45WRYJNA3IO4FWTYFXNTDHBEWMDGU465I")
	+ thex_memcmp(buf + 1, 254, "SCIIV6BFYPFII5GNTWGEEAPBUEXJOYYP67LOGVA")
	+ thex_memcmp(buf + 1, 255, "UMC6OJR6MLYN6ZWWL2XM5CQC7TROEI4P67C4PJQ")
	+ thex_memcmp(buf + 2, 254, "7HJFO26TDOH6T3KDYSI37EAXLYN3KL6SZBQLZMY");
}	


TESTS(thex_test)
    TEST(test_standard_hashes);
    TEST(test_extra_hashes);
    TEST(test_pavelch_hashes);
ENDTESTS
