2015-06-19 15:06:14 -04:00
|
|
|
//
|
|
|
|
// FILE: DistanceTable.cpp
|
|
|
|
// AUTHOR: Rob Tillaart
|
2022-07-22 16:43:52 -04:00
|
|
|
// VERSION: 0.3.1
|
2020-11-27 05:10:47 -05:00
|
|
|
// PURPOSE: Arduino library to store a symmetrical distance table in less memory
|
|
|
|
// URL: https://github.com/RobTillaart/DistanceTable
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2021-10-26 06:22:01 -04:00
|
|
|
// HISTORY
|
|
|
|
// 0.1.00 initial version
|
|
|
|
// 0.1.01 refactor
|
2022-01-07 08:29:16 -05:00
|
|
|
// 0.1.2 fix overflow; add some error detection; revert float to float to memory
|
2021-10-26 06:22:01 -04:00
|
|
|
// 0.1.3 2017-07-27 Fix issue #33
|
|
|
|
// 0.1.4 2019-01-10 add size()
|
|
|
|
// 0.1.5 2020-06-07 fix library.json, minor edits
|
2022-01-07 08:29:16 -05:00
|
|
|
// 0.1.6 2020-12-20 Arduino-CI + unit test
|
2021-01-29 06:31:58 -05:00
|
|
|
// 0.2.0 2021-01-19 refactor
|
2022-01-07 08:29:16 -05:00
|
|
|
// properly named functions,
|
2021-01-29 06:31:58 -05:00
|
|
|
// add setAll(), minimum(), maximum() and count()
|
2021-10-26 06:22:01 -04:00
|
|
|
// 0.2.1 2021-10-26 update build-CI, update readme.md
|
|
|
|
// default value in constructor
|
2022-01-07 08:29:16 -05:00
|
|
|
// 0.2.2 2021-12-17 update license, readme, minor edits
|
|
|
|
// 0.3.0 2022-01-06 add invert flag, add unit tests
|
|
|
|
// add countAbove(), countBelow()
|
|
|
|
// fix allocation + # elements
|
2022-07-22 16:43:52 -04:00
|
|
|
// 0.3.1 2022-07-22 fix set() get() test order
|
|
|
|
// set() returns bool on success.
|
|
|
|
// adds sum() and average()
|
|
|
|
// add Pascal Triangle example.
|
2022-01-07 08:29:16 -05:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2015-06-19 15:06:14 -04:00
|
|
|
|
|
|
|
#include "DistanceTable.h"
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2021-10-26 06:22:01 -04:00
|
|
|
DistanceTable::DistanceTable(uint8_t dimension, float value)
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2022-07-22 16:43:52 -04:00
|
|
|
// ATMEL 328 has ~2000 bytes RAM,
|
|
|
|
// so roughly 30 x 30 = 900 floats (4 bytes) => 1740 bytes is max feasible
|
|
|
|
// no check as other platforms allow larger tables
|
2022-01-07 08:29:16 -05:00
|
|
|
_invert = false;
|
|
|
|
_distanceTable = NULL;
|
|
|
|
_dimension = dimension;
|
2021-01-29 06:31:58 -05:00
|
|
|
_elements = 0;
|
|
|
|
|
2022-01-07 08:29:16 -05:00
|
|
|
if (dimension < 2) return;
|
|
|
|
_elements = (_dimension * (_dimension - 1)) / 2;
|
2021-01-29 06:31:58 -05:00
|
|
|
_distanceTable = (float *) malloc(_elements * sizeof(float));
|
|
|
|
if (_distanceTable == NULL)
|
|
|
|
{
|
|
|
|
_dimension = 0;
|
|
|
|
_elements = 0;
|
|
|
|
}
|
2021-10-26 06:22:01 -04:00
|
|
|
setAll(value);
|
2015-06-19 15:06:14 -04:00
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2015-06-19 15:06:14 -04:00
|
|
|
DistanceTable::~DistanceTable()
|
|
|
|
{
|
2021-01-29 06:31:58 -05:00
|
|
|
if (_distanceTable != NULL)
|
|
|
|
{
|
|
|
|
free(_distanceTable);
|
|
|
|
}
|
2015-06-19 15:06:14 -04:00
|
|
|
}
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
|
|
|
void DistanceTable::setAll(float value)
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2021-01-29 06:31:58 -05:00
|
|
|
for (uint16_t index = 0; index < _elements; index++)
|
|
|
|
{
|
|
|
|
_distanceTable[index] = value;
|
|
|
|
}
|
2015-06-19 15:06:14 -04:00
|
|
|
};
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2022-07-22 16:43:52 -04:00
|
|
|
bool DistanceTable::set(uint8_t x, uint8_t y, float value )
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2021-10-26 06:22:01 -04:00
|
|
|
// comment next line to skip range check (squeeze performance)
|
2022-07-22 16:43:52 -04:00
|
|
|
if ( (x >= _dimension) || (y >= _dimension)) return false;
|
|
|
|
if (x == y) return (value == 0.0);
|
2017-07-27 10:24:22 -04:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
if ( x < y )
|
|
|
|
{
|
|
|
|
uint8_t t = x; x = y; y = t; // swap
|
2022-01-07 08:29:16 -05:00
|
|
|
if (_invert) value = -value;
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
// prevent overflow by moving to 16 bit
|
|
|
|
uint16_t index = x;
|
|
|
|
index = (index * (index - 1)) / 2 + y;
|
|
|
|
_distanceTable[index] = value;
|
2022-07-22 16:43:52 -04:00
|
|
|
return true;
|
2015-06-19 15:06:14 -04:00
|
|
|
};
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2017-07-27 10:24:22 -04:00
|
|
|
float DistanceTable::get (uint8_t x, uint8_t y)
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2021-10-26 06:22:01 -04:00
|
|
|
// comment next line to skip range check (squeeze performance)
|
2021-01-29 06:31:58 -05:00
|
|
|
if ( (x >= _dimension) || (y >= _dimension)) return -1; // NAN ?
|
2022-07-22 16:43:52 -04:00
|
|
|
if ( x == y ) return 0.0;
|
2021-01-29 06:31:58 -05:00
|
|
|
|
2022-07-22 16:43:52 -04:00
|
|
|
bool flag = false;
|
2021-01-29 06:31:58 -05:00
|
|
|
if ( x < y )
|
|
|
|
{
|
|
|
|
uint8_t t = x; x = y; y = t;
|
2022-01-07 08:29:16 -05:00
|
|
|
flag = true;
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
uint16_t index = x;
|
|
|
|
index = (index * (index-1))/2 + y;
|
2022-01-07 08:29:16 -05:00
|
|
|
float value = _distanceTable[index];
|
|
|
|
if (_invert && flag) value = -value;
|
|
|
|
return value;
|
2021-01-29 06:31:58 -05:00
|
|
|
};
|
|
|
|
|
2017-07-27 10:24:22 -04:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
// triangular dump
|
|
|
|
void DistanceTable::dump(Print * stream)
|
|
|
|
{
|
|
|
|
stream->println();
|
2022-01-07 08:29:16 -05:00
|
|
|
for (uint8_t i = 0; i < _dimension; i++)
|
2021-01-29 06:31:58 -05:00
|
|
|
{
|
2022-01-07 08:29:16 -05:00
|
|
|
for (uint8_t j = 0; j <_dimension; j++)
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2022-01-07 08:29:16 -05:00
|
|
|
stream->print(get(i, j));
|
2021-01-29 06:31:58 -05:00
|
|
|
stream->print("\t");
|
2015-06-19 15:06:14 -04:00
|
|
|
}
|
2021-01-29 06:31:58 -05:00
|
|
|
stream->println();
|
|
|
|
}
|
|
|
|
stream->println();
|
2015-06-19 15:06:14 -04:00
|
|
|
};
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
|
|
|
|
float DistanceTable::minimum(uint8_t &x, uint8_t &y)
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2021-01-29 06:31:58 -05:00
|
|
|
float mi = _distanceTable[0];
|
2022-01-07 08:29:16 -05:00
|
|
|
x = 0;
|
|
|
|
y = 0;
|
2021-01-29 06:31:58 -05:00
|
|
|
for (uint8_t xx = 1; xx < _dimension; xx++)
|
|
|
|
{
|
|
|
|
uint16_t index = (xx * (xx - 1))/2;
|
|
|
|
for (uint8_t yy = 0; yy < xx; yy++)
|
2015-06-19 15:06:14 -04:00
|
|
|
{
|
2021-01-29 06:31:58 -05:00
|
|
|
float value = _distanceTable[index + yy];
|
|
|
|
if (value < mi)
|
|
|
|
{
|
|
|
|
mi = value;
|
|
|
|
x = xx;
|
|
|
|
y = yy;
|
|
|
|
}
|
2022-01-07 08:29:16 -05:00
|
|
|
else if (_invert)
|
|
|
|
{
|
|
|
|
value = -value;
|
|
|
|
if (value < mi)
|
|
|
|
{
|
|
|
|
mi = value;
|
|
|
|
x = yy;
|
|
|
|
y = xx;
|
|
|
|
}
|
|
|
|
}
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return mi;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float DistanceTable::maximum(uint8_t &x, uint8_t &y)
|
|
|
|
{
|
|
|
|
float ma = _distanceTable[0];
|
2022-01-07 08:29:16 -05:00
|
|
|
x = 0;
|
|
|
|
y = 0;
|
2021-01-29 06:31:58 -05:00
|
|
|
for (uint8_t xx = 1; xx < _dimension; xx++)
|
|
|
|
{
|
|
|
|
uint16_t index = (xx * (xx - 1))/2;
|
|
|
|
for (uint8_t yy = 0; yy < xx; yy++)
|
|
|
|
{
|
|
|
|
float value = _distanceTable[index + yy];
|
|
|
|
if (value > ma)
|
|
|
|
{
|
|
|
|
ma = value;
|
|
|
|
x = xx;
|
|
|
|
y = yy;
|
|
|
|
}
|
2022-01-07 08:29:16 -05:00
|
|
|
else if (_invert)
|
|
|
|
{
|
|
|
|
value = -value;
|
|
|
|
if (value > ma)
|
|
|
|
{
|
|
|
|
ma = value;
|
|
|
|
x = yy;
|
|
|
|
y = xx;
|
|
|
|
}
|
|
|
|
}
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ma;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-07-22 16:43:52 -04:00
|
|
|
float DistanceTable::sum()
|
|
|
|
{
|
|
|
|
float sum = 0;
|
|
|
|
for (uint16_t index = 0; index < _elements; index++)
|
|
|
|
{
|
|
|
|
sum += _distanceTable[index];
|
|
|
|
}
|
|
|
|
return sum * 2; // double it as it is symmetrical
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
float DistanceTable::average()
|
|
|
|
{
|
|
|
|
return sum() / _elements;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
uint16_t DistanceTable::count(float value, float epsilon)
|
|
|
|
{
|
|
|
|
uint16_t cnt = 0;
|
|
|
|
for (uint16_t index = 0; index < _elements; index++)
|
|
|
|
{
|
2022-01-07 08:29:16 -05:00
|
|
|
float current = _distanceTable[index];
|
|
|
|
if (abs (current - value) < epsilon) cnt++;
|
|
|
|
else if (_invert)
|
|
|
|
{
|
|
|
|
if (abs (current + value) < epsilon) cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_invert) return cnt;
|
|
|
|
return cnt * 2; // count the symmetrical elements too.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t DistanceTable::countAbove(float value)
|
|
|
|
{
|
|
|
|
uint16_t cnt = 0;
|
|
|
|
for (uint16_t index = 0; index < _elements; index++)
|
|
|
|
{
|
|
|
|
float current = _distanceTable[index];
|
|
|
|
if (current > value) cnt++;
|
|
|
|
else if (_invert)
|
|
|
|
{
|
|
|
|
if (-current > value) cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_invert) return cnt;
|
|
|
|
return cnt * 2; // count the symmetrical elements too.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t DistanceTable::countBelow(float value)
|
|
|
|
{
|
|
|
|
uint16_t cnt = 0;
|
|
|
|
for (uint16_t index = 0; index < _elements; index++)
|
|
|
|
{
|
|
|
|
float current = _distanceTable[index];
|
|
|
|
if (current < value) cnt++;
|
|
|
|
else if (_invert)
|
|
|
|
{
|
|
|
|
if (-current < value) cnt++;
|
|
|
|
}
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
2022-01-07 08:29:16 -05:00
|
|
|
if (_invert) return cnt;
|
|
|
|
return cnt * 2; // count the symmetrical elements too.
|
2021-01-29 06:31:58 -05:00
|
|
|
}
|
|
|
|
|
2015-06-19 15:06:14 -04:00
|
|
|
|
2021-01-29 06:31:58 -05:00
|
|
|
// --- END OF FILE ---
|
2021-12-17 04:50:55 -05:00
|
|
|
|