Skip to content

Commit 0305de9

Browse files
authored
add multi type multiMap (#10)
- add multi type **multiMap<T1, T2>** to reduce RAM and speed up lookup. - add multi type **multiMapBS<T1, T2>** binary search version. - add example for multi type - update examples - update readme.md - minor edits
1 parent 59fc478 commit 0305de9

File tree

14 files changed

+396
-85
lines changed

14 files changed

+396
-85
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.2.0] - 2023-11-12
10+
- add multi type **multiMap<T1, T2>** to reduce RAM and speed up lookup.
11+
- add multi type **multiMapBS<T1, T2>** binary search version.
12+
- add example for multi type
13+
- update examples
14+
- update readme.md
15+
- minor edits
16+
17+
----
18+
919
## [0.1.7] - 2023-06-24
1020
- add **multiMapCache()**, experimental version that caches the last value.
1121
to be used with input that do not change often.
@@ -14,7 +24,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1424
- add examples
1525
- major rewrite readme.md
1626

17-
1827
## [0.1.6] - 2022-11-17
1928
- add RP2040 in build-CI
2029
- add changelog.md
@@ -31,7 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3140
- fix Arduino-lint
3241

3342
## [0.1.3] - 2021-01-02
34-
- add Arduino-CI
43+
- add Arduino-CI
3544

3645
## [0.1.2] - 2020-06-19
3746
- fix library.json

MultiMap.h

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,57 @@
22
//
33
// FILE: MultiMap.h
44
// AUTHOR: Rob Tillaart
5-
// VERSION: 0.1.7
5+
// VERSION: 0.2.0
66
// DATE: 2011-01-26
77
// PURPOSE: Arduino library for fast non-linear mapping or interpolation of values
88
// URL: https://github.com/RobTillaart/MultiMap
99
// URL: http://playground.arduino.cc/Main/MultiMap
1010

1111

1212

13-
#define MULTIMAP_LIB_VERSION (F("0.1.7"))
13+
#define MULTIMAP_LIB_VERSION (F("0.2.0"))
1414

1515

1616
#include "Arduino.h"
1717

1818

19-
// note: the in array must have increasing values
19+
////////////////////////////////////////////////////////////////////////
20+
//
21+
// SINGLE TYPE MULTIMAP - LINEAR SEARCH - the reference
22+
//
23+
// note: the in array must have increasing values
2024
template<typename T>
2125
T multiMap(T value, T* _in, T* _out, uint8_t size)
2226
{
2327
// output is constrained to out array
2428
if (value <= _in[0]) return _out[0];
2529
if (value >= _in[size-1]) return _out[size-1];
2630

27-
// search right interval
31+
// search right interval
2832
uint8_t pos = 1; // _in[0] already tested
2933
while(value > _in[pos]) pos++;
3034

31-
// this will handle all exact "points" in the _in array
35+
// this will handle all exact "points" in the _in array
3236
if (value == _in[pos]) return _out[pos];
3337

34-
// interpolate in the right segment for the rest
38+
// interpolate in the right segment for the rest
3539
return (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
3640
}
3741

3842

39-
// performance optimized version if inputs do not change often
40-
// e.g. 2 2 2 2 2 3 3 3 3 5 5 5 5 5 5 8 8 8 8 5 5 5 5 5
41-
// implements a minimal cache of the lastValue.
43+
////////////////////////////////////////////////////////////////////////
44+
//
45+
// SINGLE TYPE MULTIMAP CACHE - LINEAR SEARCH
4246
//
4347
// note: the in array must have increasing values
48+
// performance optimized version if inputs do not change often
49+
// e.g. 2 2 2 2 2 3 3 3 3 5 5 5 5 5 5 8 8 8 8 5 5 5 5 5
50+
// implements a minimal cache of the lastValue.
4451
template<typename T>
4552
T multiMapCache(T value, T* _in, T* _out, uint8_t size)
4653
{
4754
static T lastValue = -1;
48-
static T cache = -1;
55+
static T cache = -1;
4956

5057
if (value == lastValue)
5158
{
@@ -65,36 +72,92 @@ T multiMapCache(T value, T* _in, T* _out, uint8_t size)
6572
else
6673
{
6774
// search right interval; index 0 _in[0] already tested
68-
uint8_t pos = 1;
75+
uint8_t pos = 1;
6976
while(value > _in[pos]) pos++;
70-
71-
// this will handle all exact "points" in the _in array
72-
if (value == _in[pos])
77+
78+
// this will handle all exact "points" in the _in array
79+
if (value == _in[pos])
7380
{
7481
cache = _out[pos];
7582
}
7683
else
7784
{
78-
// interpolate in the right segment for the rest
85+
// interpolate in the right segment for the rest
7986
cache = (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
8087
}
8188
}
8289
return cache;
8390
}
8491

8592

86-
// binary search version, should be faster for size > 10
93+
////////////////////////////////////////////////////////////////////////
94+
//
95+
// SINGLE TYPE MULTIMAP - BINARY SEARCH
96+
//
97+
// should be faster for size >= 10
8798
// (rule of thumb)
8899
//
89-
// note: the in array must have increasing values
100+
// note: the in array must have increasing values
90101
template<typename T>
91-
T multiMapBS(T value, T* _in, T* _out, uint16_t size)
102+
T multiMapBS(T value, T* _in, T* _out, uint8_t size)
103+
{
104+
// output is constrained to out array
105+
if (value <= _in[0]) return _out[0];
106+
if (value >= _in[size-1]) return _out[size-1];
107+
108+
// Binary Search, uint16_t needed to prevent overflow.
109+
uint16_t lower = 0;
110+
uint16_t upper = size - 1;
111+
while (lower < upper - 1)
112+
{
113+
uint8_t mid = (lower + upper) / 2;
114+
if (value >= _in[mid]) lower = mid;
115+
else upper = mid;
116+
}
117+
118+
return (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]) + _out[lower];
119+
}
120+
121+
122+
////////////////////////////////////////////////////////////////////////
123+
//
124+
// MULTITYPE MULTIMAP - LINEAR SEARCH
125+
//
126+
// note: the in array must have increasing values
127+
template<typename T1, typename T2>
128+
T2 multiMap(T1 value, T1* _in, T2* _out, uint8_t size)
129+
{
130+
// output is constrained to out array
131+
if (value <= _in[0]) return _out[0];
132+
if (value >= _in[size-1]) return _out[size-1];
133+
134+
// search right interval
135+
uint16_t pos = 1; // _in[0] already tested
136+
while(value > _in[pos]) pos++;
137+
138+
// this will handle all exact "points" in the _in array
139+
if (value == _in[pos]) return _out[pos];
140+
141+
// interpolate in the right segment for the rest
142+
return (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
143+
}
144+
145+
146+
////////////////////////////////////////////////////////////////////////
147+
//
148+
// MULTITYPE MULTIMAP - BINARY SEARCH
149+
// should be faster for size >= 10
150+
// (rule of thumb)
151+
//
152+
// note: the in array must have increasing values
153+
template<typename T1, typename T2>
154+
T2 multiMapBS(T1 value, T1* _in, T2* _out, uint8_t size)
92155
{
93156
// output is constrained to out array
94157
if (value <= _in[0]) return _out[0];
95158
if (value >= _in[size-1]) return _out[size-1];
96159

97-
// Binary Search
160+
// Binary Search, uint16_t needed to prevent overflow.
98161
uint16_t lower = 0;
99162
uint16_t upper = size - 1;
100163
while (lower < upper - 1)

README.md

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
[![Arduino CI](https://github.com/RobTillaart/MultiMap/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)
33
[![Arduino-lint](https://github.com/RobTillaart/MultiMap/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/MultiMap/actions/workflows/arduino-lint.yml)
44
[![JSON check](https://github.com/RobTillaart/MultiMap/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/MultiMap/actions/workflows/jsoncheck.yml)
5+
[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/MultiMap.svg)](https://github.com/RobTillaart/MultiMap/issues)
6+
57
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MultiMap/blob/master/LICENSE)
68
[![GitHub release](https://img.shields.io/github/release/RobTillaart/MultiMap.svg?maxAge=3600)](https://github.com/RobTillaart/MultiMap/releases)
9+
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/MultiMap.svg)](https://registry.platformio.org/libraries/robtillaart/MultiMap)
710

811

912
# MultiMap
@@ -39,7 +42,25 @@ Of course this approximation introduces an error.
3942
By increasing the number of points and choose their position strategically the average error will be reduced.
4043

4144
Note: some functions are hard to approximate with multiMap as they go to infinity or have a singularity.
42-
Think of **tan(x)** around x = PI/2 (90°) or **sin(1/x)** around zero.
45+
Think of **tan(x)** around x = PI/2 (90°) or **sin(1/x)** around zero.
46+
47+
48+
#### Related
49+
50+
Other mapping libraries
51+
52+
- https://github.com/RobTillaart/FastMap
53+
- https://github.com/RobTillaart/Gamma
54+
- https://github.com/RobTillaart/map2colour
55+
- https://github.com/RobTillaart/moduloMap
56+
- https://github.com/RobTillaart/MultiMap
57+
58+
59+
## Interface
60+
61+
```cpp
62+
#include "MultiMap.h"
63+
```
4364

4465

4566
#### Usage
@@ -66,7 +87,7 @@ This is a explicit difference with the **map()** function.
6687
Therefore it is important to extend the range of the arrays to cover all possible values.
6788

6889

69-
#### Performance
90+
## Performance
7091

7192
**multiMap()** does a linear search for the inputValue in the inputArray.
7293
This implies that usage of larger and more precise arrays will take more time.
@@ -96,24 +117,59 @@ Experimental 0.1.7 => use with care.
96117

97118
**multiMapCache()** MMC for short, is a very similar function as **multiMap()**.
98119
The main difference is that MMC caches the last input and output value.
99-
The goal is to improve the performance by preventing
120+
The goal is to improve the performance by preventing searching the same
121+
value again and again.
100122

101123
If the input sequence has a lot of repeating values e.g. 2 2 2 2 2 2 5 5 5 5 5 4 4 4 4 2 2 2 2 2 2
102124
MMC will be able to return the value from cache often.
103125
Otherwise keeping cache is overhead.
104126

105127
Be sure to do your own tests to see if MMC improves your performance.
106128

129+
A possible variation is to cache the last interval - lower and upper index.
130+
It would allow a to test that value and improve the linear search.
131+
(to be investigated).
107132

108-
#### Related
109133

110-
Other mapping libraries
134+
#### MultiMap two types
111135

112-
- https://github.com/RobTillaart/FastMap
113-
- https://github.com/RobTillaart/Gamma
114-
- https://github.com/RobTillaart/map2colour
115-
- https://github.com/RobTillaart/moduloMap
116-
- https://github.com/RobTillaart/MultiMap
136+
Experimental 0.2.0 => use with care.
137+
138+
**multiMap<T1, T2>()** MMTT for short, is a very similar function as **multiMap()**.
139+
The main difference is that MMTT uses two different types, typical the input
140+
is an integer type and the output is a float or double type.
141+
It is expected that there will be a gain if two different sized integer types are used.
142+
This is not tested.
143+
144+
See the example **multimap_distance_two_types.ino**
145+
146+
```cpp
147+
// for a sharp distance range finder
148+
float sharp2cm2(int val)
149+
{
150+
// out[] holds the distances in cm
151+
float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20};
152+
153+
// in[] holds the measured analogRead() values for that distance
154+
int in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506};
155+
156+
float dist = multiMap<int, float>(val, in, out, 14);
157+
return dist;
158+
}
159+
```
160+
161+
A first test indicate that using the int type for the input in the example
162+
is substantial (~37%) faster per call. Test on UNO, time in micros per call.
163+
164+
| types | time us | call |
165+
|:-------:|:---------:|:-------|
166+
| 1 | 194.93 | ```float dist = multiMap<float>(val, in, out, 14);``` |
167+
| 2 | 121.97 | ```float dist = multiMap<int, float>(val, in, out, 14);``` |
168+
169+
Furthermore it is obvious that there is less need for RAM if the integer type is smaller
170+
in size than the float type.
171+
172+
Be sure to do your own tests to see if MMTT improves your performance.
117173
118174
119175
## Operation
@@ -129,22 +185,17 @@ Please note the fail example as this shows that in the intern math overflow can
129185
130186
- improve documentation
131187
132-
133188
#### Should
134189
135190
- investigate multiMapCache behaviour
136191
- determine overhead.
137-
- investigate binary search multiMapBS behaviour
138-
- expect a constant time
139-
- where is the tipping point between linear and binary search.
140-
(expect around size = 8)
141192
- extend unit tests
142-
193+
- multi type versions
143194
144195
#### Could
145196
146197
- Investigate class implementation
147-
- basic call out = mm.map(value);
198+
- basic call ```out = mm.map(value);```
148199
- runtime adjusting input and output array **begin(in[], out[])**
149200
- performance / footprint
150201
- less parameter passing
@@ -154,14 +205,19 @@ Please note the fail example as this shows that in the intern math overflow can
154205
now it is constrained without user being informed.
155206
- Investigate a 2D multiMap e.g. for complex numbers?
156207
- is it possible / feasible?
157-
- data type input array does not need to be equal to the output array.
158-
- template<typename T1, typename T2>
159-
```T2 multiMapBS(T1 value, T1* _in, T2* _out, uint16_t size)```
160-
161208
162209
#### Wont
163210
164211
- should the lookup tables be merged into one array of pairs?
165212
- you cannot reuse e.g. the input array or the output array then.
166213
this would not improve the memory footprint.
167214
215+
216+
## Support
217+
218+
If you appreciate my libraries, you can support the development and maintenance.
219+
Improve the quality of the libraries by providing issues and Pull Requests, or
220+
donate through PayPal or GitHub sponsors.
221+
222+
Thank you,
223+

examples/multimap_BS_compare/multimap_BS_compare.ino

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@
44
// PURPOSE: demo
55
// DATE: 2023-06-23
66

7+
78
#include "MultiMap.h"
89

910
long in[100];
1011
long out[100];
1112

1213
volatile int x;
1314

15+
1416
void setup()
1517
{
1618
Serial.begin(115200);
1719
Serial.println(__FILE__);
20+
Serial.print("MULTIMAP_LIB_VERSION: ");
21+
Serial.println(MULTIMAP_LIB_VERSION);
1822
Serial.println();
23+
delay(100);
1924

2025
for (int i = 0; i < 100; i++)
2126
{

0 commit comments

Comments
 (0)