blob: a3f5d4fe5dc6dcd86e4f484e2d8343893b4e737a [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed 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.
*/
/**
* @file
* This file implements a functional test for the Verhoeff objects.
*
*/
#ifdef VERHOEFF_TEST
#ifdef WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "Verhoeff.h"
class VerhoeffTest
{
public:
VerhoeffTest();
uint64_t duration;
int TestCount_SDE;
int SuccessCount_SDE;
int TestCount_ATE;
int SuccessCount_ATE;
int TestCount_JTE;
int SuccessCount_JTE;
void Reset();
template<class Verhoeff>
void RunTests(int strLen, int division, int divisions, bool runBasicTests = true);
template<class Verhoeff>
void SearchPermutations(int strLen, int division, int divisions);
private:
template<class Verhoeff>
char *MakeTestStrings(int strLen, int division, int divisions);
template<class Verhoeff>
void MakeTestString(char *buf, int strLen, int val);
template<class Verhoeff>
void TestSingleDigitErrors(char *str, size_t strLen);
template<class Verhoeff>
void TestAdjacentTranspositions(char *str, size_t strLen);
template<class Verhoeff>
void TestJumpTranspositions(char *str, size_t strLen);
template<class Verhoeff>
void PermutePermutationTable();
static int RandRange(int mod);
static inline uint64_t Now()
{
#ifdef WIN32
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return (((uint64_t)ft.dwHighDateTime) << 32 | ft.dwLowDateTime) / 10000;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
}
};
VerhoeffTest::VerhoeffTest()
{
Reset();
}
void VerhoeffTest::Reset()
{
duration = 0;
TestCount_SDE = 0;
SuccessCount_SDE = 0;
TestCount_ATE = 0;
SuccessCount_ATE = 0;
TestCount_JTE = 0;
SuccessCount_JTE = 0;
}
template <class Verhoeff>
void VerhoeffTest::RunTests(int strLen, int division, int divisions, bool runBasicTests)
{
Reset();
char *testStrings = MakeTestStrings<Verhoeff>(strLen, division, divisions);
uint64_t start = Now();
char *curStr = testStrings;
while (*curStr != 0)
{
size_t len = strlen(curStr);
if (runBasicTests)
{
TestSingleDigitErrors<Verhoeff>(curStr, len);
TestAdjacentTranspositions<Verhoeff>(curStr, len);
}
TestJumpTranspositions<Verhoeff>(curStr, len);
curStr += len + 1;
}
duration = Now() - start;
free(testStrings);
}
template<class Verhoeff>
void VerhoeffTest::SearchPermutations(int strLen, int division, int divisions)
{
char *str = (char *)malloc(strLen + 1);
int sdeFails = 0;
int ateFails = 0;
int bestJTE = -1;
srand(time(NULL));
int totalStrs = Verhoeff::Base;
for (int i = 2; i < strLen; i++)
totalStrs *= Verhoeff::Base;
int startVal = (totalStrs * division) / divisions;
int endVal = (totalStrs * (division + 1)) / divisions;
for (int permCount = 0; true; permCount++)
{
if (permCount != 0 && (permCount % 100000) == 0)
printf("%d complete (%d, %d)\n", permCount, sdeFails, ateFails);
Reset();
for (int val = startVal; val < endVal; val++)
{
MakeTestString<Verhoeff>(str, strLen, val);
#if WEAVE_CONFIG_VERHOEFF_TEST_SINGLE_DIGIT_ERRORS
TestSingleDigitErrors<Verhoeff>(str, strLen);
if (SuccessCount_SDE != TestCount_SDE)
{
sdeFails++;
goto next_perm;
}
#endif // WEAVE_CONFIG_VERHOEFF_TEST_SINGLE_DIGIT_ERRORS
TestAdjacentTranspositions<Verhoeff>(str, strLen);
if (SuccessCount_ATE != TestCount_ATE)
{
ateFails++;
goto next_perm;
}
TestJumpTranspositions<Verhoeff>(str, strLen);
}
if (SuccessCount_JTE > bestJTE)
{
bestJTE = SuccessCount_JTE;
printf("%u/%u : ", SuccessCount_JTE, TestCount_JTE);
for (int i = 0; i < Verhoeff::Base; i++)
printf("%2u, ", (unsigned)Verhoeff::sPermTable[i]);
printf("\n");
}
next_perm:
PermutePermutationTable<Verhoeff>();
}
}
template <class Verhoeff>
char *VerhoeffTest::MakeTestStrings(int strLen, int division, int divisions)
{
if (strLen < 2)
return NULL;
int totalStrs = Verhoeff::Base;
for (int i = 2; i < strLen; i++)
totalStrs *= Verhoeff::Base;
int startVal = (totalStrs * division) / divisions;
int endVal = (totalStrs * (division + 1)) / divisions;
char *strTable = (char *)malloc((strLen + 1) * (endVal - startVal) + 1);
char *curStr = strTable;
for (int val = startVal; val < endVal; val++, curStr += strLen + 1)
MakeTestString<Verhoeff>(curStr, strLen, val);
// zero-length string marks end of table
*curStr = 0;
return strTable;
}
template <class Verhoeff>
void VerhoeffTest::MakeTestString(char *buf, int strLen, int val)
{
int x = val;
for (int i = strLen - 2; i >= 0; i--)
{
int charVal = x % Verhoeff::Base;
buf[i] = Verhoeff::ValToChar(charVal);
x = x / Verhoeff::Base;
}
buf[strLen-1] = Verhoeff::ComputeCheckChar(buf, strLen - 1);
buf[strLen] = 0;
}
template <class Verhoeff>
void VerhoeffTest::TestSingleDigitErrors(char *str, size_t strLen)
{
char *p = str;
while (*p)
{
char origChar = *p;
int origVal = Verhoeff::CharToVal(origChar);
int testVal = origVal;
while (true)
{
testVal++;
if (testVal == Verhoeff::Base)
testVal = 0;
if (testVal == origVal)
break;
*p = Verhoeff::ValToChar(testVal);
TestCount_SDE++;
if (!Verhoeff::ValidateCheckChar(str, strLen))
SuccessCount_SDE++;
}
*p = origChar;
p++;
}
}
template <class Verhoeff>
void VerhoeffTest::TestAdjacentTranspositions(char *str, size_t strLen)
{
for (size_t i = 1; i < strLen; i++)
{
char a = str[i-1], b = str[i];
if (a == b)
continue;
str[i-1] = b;
str[i] = a;
TestCount_ATE++;
if (!Verhoeff::ValidateCheckChar(str, strLen))
SuccessCount_ATE++;
str[i-1] = a;
str[i] = b;
}
}
template <class Verhoeff>
void VerhoeffTest::TestJumpTranspositions(char *str, size_t strLen)
{
for (size_t i = 2; i < strLen; i++)
{
char a = str[i-2], b = str[i];
if (a == b)
continue;
str[i-2] = b;
str[i] = a;
TestCount_JTE++;
if (!Verhoeff::ValidateCheckChar(str, strLen))
SuccessCount_JTE++;
str[i-2] = a;
str[i] = b;
}
}
template<class Verhoeff>
void VerhoeffTest::PermutePermutationTable()
{
for (int i = Verhoeff::Base - 1; i > 0; i--)
{
int rnd = RandRange(i + 1);
uint8_t swap = Verhoeff::sPermTable[i];
Verhoeff::sPermTable[i] = Verhoeff::sPermTable[rnd];
Verhoeff::sPermTable[rnd] = swap;
}
}
int VerhoeffTest::RandRange(int limit)
{
int max = RAND_MAX - (RAND_MAX % limit);
int rnd;
do
rnd = rand();
while (rnd >= max);
return rnd % limit;
}
extern "C" int main(int argc, char *argv[])
{
typedef Verhoeff10 V;
char *cmd = strrchr(argv[0], '/');
#ifdef WIN32
if (cmd == NULL)
cmd = strrchr(argv[0], '\\');
if (cmd == NULL)
cmd = strrchr(argv[0], '\\');
#endif
if (cmd == NULL)
cmd = argv[0];
if (argc > 1 && strcmp(argv[1], "generate") == 0)
{
if (argc != 3)
{
fprintf(stderr, "Usage: %s generate <string>\n", cmd);
return -1;
}
char checkChar = V::ComputeCheckChar(argv[2]);
if (checkChar == 0)
{
fprintf(stderr, "Invalid character in input\n");
return -1;
}
printf("%s%c\n", argv[2], checkChar);
}
else if (argc > 1 && strcmp(argv[1], "verify") == 0)
{
if (argc != 3)
{
fprintf(stderr, "Usage: %s verify <string>\n", cmd);
return -1;
}
if (V::ValidateCheckChar(argv[2]))
{
printf("%s is VALID\n", argv[2]);
return 0;
}
else
{
printf("%s is INVALID\n", argv[2]);
return -1;
}
}
else if (argc > 1 && strcmp(argv[1], "test") == 0)
{
VerhoeffTest test;
if (argc != 3)
{
fprintf(stderr, "Usage: %s test <base(base-10/base-16/base-32/base-36)>\n", cmd);
return -1;
}
const char *testStr = "123456";
char verifyStr[10] = "";
char checkChar = '0';
// determin Verhoeff Base and run tests
if (strcmp(argv[2], "base-10") == 0)
{
printf("testing Verhoeff10\n");
// run a simple compute and validate test
checkChar = Verhoeff10::ComputeCheckChar(testStr);
sprintf(verifyStr, "%s%c", testStr, checkChar);
if (!Verhoeff10::ValidateCheckChar(verifyStr) ||
!Verhoeff10::ValidateCheckChar(checkChar, testStr))
{
printf("%s is INVALID\n", verifyStr);
return -1;
}
// run fully Verhoeff tests
test.RunTests<Verhoeff10>(6, 0, 1000, true);
}
else if (strcmp(argv[2], "base-16") == 0)
{
printf("testing Verhoeff16\n");
// run a simple compute and validate test
checkChar = Verhoeff16::ComputeCheckChar(testStr);
sprintf(verifyStr, "%s%c", testStr, checkChar);
if (!Verhoeff16::ValidateCheckChar(verifyStr) ||
!Verhoeff16::ValidateCheckChar(checkChar, testStr))
{
printf("%s is INVALID\n", verifyStr);
return -1;
}
// run fully Verhoeff tests
test.RunTests<Verhoeff16>(6, 0, 1000, true);
}
else if (strcmp(argv[2], "base-32") == 0)
{
printf("testing Verhoeff32\n");
// run a simple compute and validate test
checkChar = Verhoeff32::ComputeCheckChar(testStr);
sprintf(verifyStr, "%s%c", testStr, checkChar);
if (!Verhoeff32::ValidateCheckChar(verifyStr) ||
!Verhoeff32::ValidateCheckChar(checkChar, testStr))
{
printf("%s is INVALID\n", verifyStr);
return -1;
}
// run fully Verhoeff tests
test.RunTests<Verhoeff32>(6, 0, 1000, true);
}
else if (strcmp(argv[2], "base-36") == 0)
{
printf("testing Verhoeff36\n");
// run a simple compute and validate test
checkChar = Verhoeff36::ComputeCheckChar(testStr);
sprintf(verifyStr, "%s%c", testStr, checkChar);
if (!Verhoeff36::ValidateCheckChar(verifyStr) ||
!Verhoeff36::ValidateCheckChar(checkChar, testStr))
{
printf("%s is INVALID\n", verifyStr);
return -1;
}
// run fully Verhoeff tests
test.RunTests<Verhoeff36>(6, 0, 1000, true);
}
else
{
printf("Invalid Verhoeff base\n");
return -1;
}
printf("duration = %lu.%03lu\n", (unsigned long)(test.duration / 1000), (unsigned long)(test.duration % 1000));
if (test.TestCount_SDE != 0)
printf("single digit errors found = %f%%\n", (double)test.SuccessCount_SDE * 100.0 / (double)test.TestCount_SDE);
else
printf("no single digit errors tested\n");
if (test.TestCount_ATE != 0)
printf("adjacent transpositions found = %f%%\n", (double)test.SuccessCount_ATE * 100.0 / (double)test.TestCount_ATE);
else
printf("no adjacent transpositions tested\n");
if (test.TestCount_JTE != 0)
printf("jump transpositions found = %f%%\n", (double)test.SuccessCount_JTE * 100.0 / (double)test.TestCount_JTE);
else
printf("no jump transpositions tested\n");
}
else if (argc > 1 && strcmp(argv[1], "search-perms") == 0)
{
VerhoeffTest test;
test.SearchPermutations<V>(3, 0, 1);
}
else
{
fprintf(stderr,
"Usage: %s <command> [ <args> ]\n"
"\n"
"Commands:\n"
" generate <string>\n"
" verify <string-with-check-digit>\n"
" test <base(base-10/base-16/base-32/base-36)>\n"
" search-perms\n",
cmd);
return -1;
}
return 0;
}
#endif // VERHOEFF_TEST