Go Down

Topic: Some new "ideas for" core functions (Read 17600 times) previous topic - next topic

HazardsMind

#15
Sep 06, 2014, 05:37 am Last Edit: Sep 06, 2014, 06:21 am by HazardsMind Reason: 1
I was playing around with my templated map function and I found out that you can have both work even with the same name.

This is valid and the results are the same as the results I attached before.
Code: [Select]

template<class type> type map(type x, type in_min, type in_max, type out_min, type out_max);

void setup()
{
  Serial.begin(115200);
  for(byte i = 0; i < 101; i++)
  {
    Serial.print(map(i, 0, 100, 0, 10));
    Serial.print("\t");
    Serial.println(map<float>(i, 0, 100, 0, 10));
    delay(10);
  }
}

template<class type> type map(type x, type in_min, type in_max, type out_min, type out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void loop() {}


@CB
Could you explain to me why the normal map function in the following sketch uses more memory then my templated map function as type long?

Code: [Select]

void setup()
{
  Serial.begin(115200);
  for(byte i = 0; i < 101; i++)
  {
     Serial.println(map(i, 0, 100, 0, 10)); // 2,794 bytes
      //Serial.println(map<long>(i, 0, 100, 0, 10)); // 2,562 bytes
  }
}

void loop() {}
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

HazardsMind

#16
Sep 06, 2014, 07:25 pm Last Edit: Sep 08, 2014, 05:32 am by HazardsMind Reason: 1
Library!

Added these files to your normal libraries folder under the sketch folder, and add #include <Core_Ext.h> like so.

Quote
#ifndef Arduino_h
#define Arduino_h

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <avr/pgmspace.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#include "binary.h"
#include <Core_Ext.h>


Save the file, and there you go.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

westfw

So how exactly do you justify wanting "mirrorbyte" to be a core function, instead of sticking it in some obscure bit-twiddling library?  (or FFT library?)

HazardsMind

#18
Sep 07, 2014, 01:29 am Last Edit: Sep 07, 2014, 01:35 am by HazardsMind Reason: 1
I put it in some obscure bit-twiddling library, just for now.

Besides, there is a one in a million chance they will ever see these functions, and a one in a billion chance these functions will be added.

We have been asking to have things added for years, and exactly how many of them were ever implemented?

If these functions help anyone, even just one person, I will be happy.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

pYro_65

Could you explain to me why the normal map function in the following sketch uses more memory then my templated map function as type long?


Your template is compiled inline.

Forum Mod anyone?
https://arduino.land/Moduino/

HazardsMind

Quote
Your template is compiled inline.
Thats it? So why aren't all if not most of the functions inline?

I have a lot of functions to change then.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

pYro_65

It'll only be of a benefit if the resulting inline code is smaller than the cost of calling/returning a function. Otherwise multiple use of an inline function may result in a larger code size. Only testing will prove the result, and then tests may be rubbish as they may not represent what happens in real use of the code.

Below is what I would do. It will do the maths using the largest type provided so there is no need to specify the value in pointy brackets. The code is more complex, however it makes it easier for the end user.

Code: [Select]
template< bool V, typename T, typename U > struct Select{ typedef T Result; };
template< typename T, typename U > struct Select< false, T, U >{ typedef U Result; };

template< typename T, typename U > struct LargerType{
  typedef typename Select< sizeof( U ) < sizeof( T ), T, U >::Result Result;
};

template< typename T, typename U, typename V, typename X, typename Y >
   T map( const T &x, const U &imin, const V &imax, const X &omin, const Y &omax ) throw()
    {
      typedef typename LargerType< typename LargerType< typename LargerType< T, U >::Result, typename LargerType< V, X >::Result >::Result, Y >::Result cll;
      return (cll)(x - imin) * (omax - omin) / (imax - imin) + omin;
    }


The C++11 version can be a little more efficient (specific cases) using move semantics:
Code: [Select]
template< typename T, typename U, typename V, typename X, typename Y >
   auto map( const T &x, U &&imin, V &&imax, X &&omin, Y &&omax ) -> T
    {
      typedef typename LargerType< typename LargerType< typename LargerType< T, U >::Result, typename LargerType< V, X >::Result >::Result, Y >::Result cll;
      return (cll)(x - imin) * (omax - omin) / (imax - imin) + omin;
    }
Forum Mod anyone?
https://arduino.land/Moduino/

SteveThackery

If we do implement the 'average' function, can we please, please, please not call it 'average'?

It was Excel that started the rot, unfortunately.  There isn't a mathematical definition for average.  Rather, there are three types of average: mean, median and  mode. 

Therefore, if this suggestion is adopted I suggest the function is called 'mean()'.

HazardsMind

#23
Sep 08, 2014, 03:29 pm Last Edit: Sep 08, 2014, 03:31 pm by HazardsMind Reason: 1

If we do implement the 'average' function, can we please, please, please not call it 'average'?

It was Excel that started the rot, unfortunately.  There isn't a mathematical definition for average.  Rather, there are three types of average: mean, median and  mode.  

Therefore, if this suggestion is adopted I suggest the function is called 'mean()'.


Ok I will change it to mean(), and I'll make median and mode too.

@Pyro
I never did look into C++11, :/ Looks like I got homework.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

SteveThackery



Ok I will change it to mean(), and I'll make median and mode too.



Fantastic, thank you!

HazardsMind

#25
Sep 08, 2014, 06:54 pm Last Edit: Sep 09, 2014, 01:31 am by HazardsMind Reason: 1
The changes are made and I added median and mode. However, I can't do any tests on my end. If someone wants to try the new library functions and post the results, that would be great.

Code: [Select]
int myArray[] = {
 25, 1000, 420, 1000, 10, 345, 862};

void setup(){
 Serial.begin(115200);
 Serial.println(mean(myArray));
 Serial.println(median(myArray));
 Serial.println(mode(myArray));
}

void loop() {

}


Note: I didn't change anything in the .cpp file, so I did not attach it.

In regards to the mode function, I need to figure out a way to see if I can change the return result if there is more than one outcome i.e. Bimodal.

Bimodal: A set of data is bimodal if it has 2 modes (i.e., two numbers that occur most often, and the same number of times).

Edit: Error in the mean() function, tmp was not set to zero, so the result was incorrect. Fixed.

Code: [Select]
int myArray[] = {
  25, 1000, 25, 1000, 1000, 345, 862};

void setup(){
  Serial.begin(115200);

  Serial.println(mean(myArray));
  Serial.println(median(myArray));
  Serial.println(mode(myArray));
}

void loop() {

}


Result:
Quote
608 // mean
862 // median
1000 // mode
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

pYro_65


@Pyro
I never did look into C++11, :/ Looks like I got homework.


I'm still learning it myself, infact I didn't realize at first, but the LargerType and Select classes in my code can be replaced with the new decltype keyword:

Code: [Select]

template< typename T, typename U, typename V, typename X, typename Y >
   auto map( const T &x, U &&imin, V &&imax, X &&omin, Y &&omax ) -> T
    {
      typedef decltype( T() * U() * V() * X() * Y() ) cll;
      return (cll)(x - imin) * (omax - omin) / (imax - imin) + omin;
    }


As integer promotion will occur with each multiplication operator, the largest type can be found.

Also a new function declaration syntax allows using the same decltype as the return type of the function; and then its sized to the largest type which will prevent overflow if the data input is of a different type to the range inputs.

Code: [Select]

template< typename T, typename U, typename V, typename X, typename Y >
   auto map( const T &x, U &&imin, V &&imax, X &&omin, Y &&omax ) -> decltype( T() * U() * V() * X() * Y() )
    {
      typedef decltype( T() * U() * V() * X() * Y() ) cll;
      return (cll)(x - imin) * (omax - omin) / (imax - imin) + omin;
    }


Another feature is ranged loops, your mean function could look like this:
Code: [Select]
template<class T, size_t N>
  T mean(T(&x)[N]){
    T tmp = 0;
    for( auto i : x )  tmp += i;
    return tmp / N;
}
Forum Mod anyone?
https://arduino.land/Moduino/

pYro_65

A big thing that will keep your code out of the core is dynamic memory usage.

GCC supports stack based allocation, and on an AVR, you should definitely use it over short lived dynamic allocations.

Quote
template<typename T, size_t N> T median(T (&Arr)[N])
{
  //T* _data = new T[N];
  T _data[N];
  sort(Arr);
  for (int i = 0; i < N; i++)
  {
    _data[i] = Arr[i];
  }
 
  T Med = 0;
  if ((N % 2) == 0)
    Med = (T)(_data[N/2] + _data[(N/2) - 1])/2;
  else
    Med = _data[N/2];

  //delete [] _data;
  return Med;
}


It is an extension, however the Arduino core uses gnu extensions as the AVR core has parts of its code which require it ( atomic block macros for example ), so its worth using. Also a simple #ifdef can be used to replace the dynamic handling for non AVR machines.
Forum Mod anyone?
https://arduino.land/Moduino/

HazardsMind

#28
Sep 09, 2014, 03:51 pm Last Edit: Sep 09, 2014, 03:55 pm by HazardsMind Reason: 1
Quote
Also a simple #ifdef can be used to replace the dynamic handling for non AVR machines.
How would I do that with this, do I just use #if defined(__AVR__) and just mod the code to use dynamic memory for non AVR machines?

Code: [Select]
template<typename T, size_t N> T median(T (&Arr)[N])
{
  sort(Arr);
 
  /*T _data = new T[N];
  for (int i = 0; i < N; i++)
  {
    _data[i] = Arr[i];
  }*/
 
  T Med = 0;
  if ((N % 2) == 0)
    Med = (T)(Arr[N/2] + Arr[(N/2) - 1])/2;//Med = (T)(_data[N/2] + _data[(N/2) - 1])/2;
  else
    Med = Arr[N/2];//Med = _data[N/2];

  //delete [] _data;
  return Med;
}


When will C++11 be usable on the arduino, its usefulness will be great appreciated.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

pYro_65

Quote
How would I do that with this, do I just use #if defined(__AVR__) and just mod the code to use dynamic memory for non AVR machines?


There is no need, my lack of sleep made me blind to the fact that N is a constant, and is legal in any C++.

For clarification, if N was a parameter to the function ( and therefore not a compile time constant in C++98 ) rather than a template parameter, things would be different.

The AVR macro is one way, however other platforms use GCC (Due) and it seems there is a __GNUC__ macro.
https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/C-Extensions.html
And the extension in question: https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Variable-Length.html#Variable-Length

Code: [Select]
#ifdef __GNUC__
 T _data[N];
#elif
 T* _data = new T[N];
#endif

//...

#ifndef __GNUC__
 delete [] _data;
#endif



Quote
When will C++11 be usable on the arduino, its usefulness will be great appreciated.


You can enable it in 1.5.7, and it'll hopefully be default in 1.5.8 ( https://github.com/arduino/Arduino/pull/2175 )
Forum Mod anyone?
https://arduino.land/Moduino/

Go Up