diff --git a/pcb_calculator/calculator_panels/panel_r_calculator.cpp b/pcb_calculator/calculator_panels/panel_r_calculator.cpp
index 0de6253672..4c7c8b4517 100644
--- a/pcb_calculator/calculator_panels/panel_r_calculator.cpp
+++ b/pcb_calculator/calculator_panels/panel_r_calculator.cpp
@@ -35,6 +35,13 @@
 wxString r_calculator_help =
 #include "r_calculator_help.h"
 
+/* RES_EQUIV_CALC class considers resistor values from a limited range
+ * and only combinations of up to 4 resistors, so target values less than 
+ * parallel combination of minimum resistors or greater than serial combination
+ * of maximum resistors cannot be reasonably looked for
+ */
+static const double min_target_value = static_cast<double>(RES_EQUIV_CALC_FIRST_VALUE) / 4;
+static const double max_target_value = static_cast<double>(RES_EQUIV_CALC_LAST_VALUE) * 4;
 
 extern double DoubleFromString( const wxString& TextValue );
 
@@ -94,22 +101,25 @@ void PANEL_R_CALCULATOR::LoadSettings( PCB_CALCULATOR_SETTINGS* aCfg )
 
 void PANEL_R_CALCULATOR::OnCalculateESeries( wxCommandEvent& event )
 {
-    double   reqr;   // required resistor stored in local copy
-    double   error, err3 = 0;
-    wxString es, fs; // error and formula strings
+    double reqr = 1000 * DoubleFromString( m_ResRequired->GetValue() );
 
-    wxBusyCursor dummy;
+    if( std::isnan( reqr ) || reqr < min_target_value || reqr > max_target_value )
+    {
+        wxMessageBox( wxString::Format( _( "Incorrect required resistance value: %s" ),
+                                        m_ResRequired->GetValue() ) );
+        return;
+    }
+    
+    wxBusyCursor busyCursor; // As long as this variable exists, the cursor will be 'busy'
 
-    reqr = ( 1000 * DoubleFromString( m_ResRequired->GetValue() ) );
-    m_eSeries.SetRequiredValue( reqr ); // keep a local copy of required resistor value
-    m_eSeries.NewCalc();                // assume all values available
+    m_eSeries.NewCalc( reqr ); // assume all values available
     /*
      * Exclude itself. For the case, a value from the available series is found as required value,
      * the calculator assumes this value needs a replacement for the reason of being not available.
      * Two further exclude values can be entered to exclude and are skipped as not being available.
      * All values entered in KiloOhms are converted to Ohm for internal calculation
      */
-    m_eSeries.Exclude( 1000 * DoubleFromString( m_ResRequired->GetValue() ) );
+    m_eSeries.Exclude( reqr );
     m_eSeries.Exclude( 1000 * DoubleFromString( m_ResExclude1->GetValue() ) );
     m_eSeries.Exclude( 1000 * DoubleFromString( m_ResExclude2->GetValue() ) );
 
@@ -117,88 +127,43 @@ void PANEL_R_CALCULATOR::OnCalculateESeries( wxCommandEvent& event )
     {
         m_eSeries.Calculate();
     }
-    catch( std::out_of_range const& exc )
+    catch( const std::exception& exc )
     {
-        wxString msg;
-        msg << "Internal error: " << exc.what();
-
-        wxMessageBox( msg );
+        wxMessageBox( wxString::Format( "Internal error: %s", exc.what() ) );
         return;
     }
 
-    fs = m_eSeries.GetResults()[RES_EQUIV_CALC::S2R].e_name; // show 2R solution formula string
-    m_ESeries_Sol2R->SetValue( fs );
-    error = reqr
-            + m_eSeries.GetResults()[RES_EQUIV_CALC::S2R].e_value; // absolute value of solution
-    error = ( reqr / error - 1 ) * 100;                 // error in percent
-
-    if( error )
+    auto showResult = [reqr]( const std::optional<RESISTANCE>& aResult, wxTextCtrl* aFormulaField,
+                              wxTextCtrl* aErrorField )
     {
-        if( std::abs( error ) < 0.01 )
-            es.Printf( "<%.2f", 0.01 );
-        else
-            es.Printf( "%+.2f", error );
-    }
-    else
-    {
-        es = _( "Exact" );
-    }
+        wxString fs, es; // formula and error string
 
-    m_ESeriesError2R->SetValue( es );                      // anyway show 2R error string
-
-    if( m_eSeries.GetResults()[RES_EQUIV_CALC::S3R].e_use ) // if 3R solution available
-    {
-        err3 = reqr + m_eSeries.GetResults()[RES_EQUIV_CALC::S3R].e_value; // calculate the 3R
-        err3 = ( reqr / err3 - 1 ) * 100;                  // error in percent
-
-        if( err3 )
+        if( aResult ) // if value is present
         {
-            if( std::abs( err3 ) < 0.01 )
+            fs = aResult->name;
+            double sol = aResult->value;
+            double error = ( sol / reqr - 1 ) * 100; // relative error in percent
+
+            if( std::abs( error ) < epsilon )
+                es = _( "Exact" );
+            else if( std::abs( error ) < 0.01 )
                 es.Printf( "<%.2f", 0.01 );
             else
-                es.Printf( "%+.2f", err3 );
+                es.Printf( "%+.2f", error );
         }
         else
         {
-            es = _( "Exact" );
+            fs = _( "Not worth using" );
+            es = wxEmptyString;
         }
 
-        m_ESeriesError3R->SetValue( es ); // show 3R error string
-        fs = m_eSeries.GetResults()[RES_EQUIV_CALC::S3R].e_name;
-        m_ESeries_Sol3R->SetValue( fs );  // show 3R formula string
-    }
-    else                                  // nothing better than 2R found
-    {
-        fs = _( "Not worth using" );
-        m_ESeries_Sol3R->SetValue( fs );
-        m_ESeriesError3R->SetValue( wxEmptyString );
-    }
+        aFormulaField->SetValue( fs );
+        aErrorField->SetValue( es );
+    };
 
-    fs = wxEmptyString;
-
-    if( m_eSeries.GetResults()[RES_EQUIV_CALC::S4R].e_use ) // show 4R solution if available
-    {
-        fs = m_eSeries.GetResults()[RES_EQUIV_CALC::S4R].e_name;
-
-        error = reqr
-                + m_eSeries.GetResults()[RES_EQUIV_CALC::S4R].e_value; // absolute value of solution
-        error = ( reqr / error - 1 ) * 100;                 // error in percent
-
-        if( error )
-            es.Printf( "%+.2f", error );
-        else
-            es = _( "Exact" );
-
-        m_ESeriesError4R->SetValue( es );
-    }
-    else // no 4R solution
-    {
-        fs = _( "Not worth using" );
-        es = wxEmptyString;
-        m_ESeriesError4R->SetValue( es );
-    }
-
-    m_ESeries_Sol4R->SetValue( fs );
+    showResult( m_eSeries.GetResults()[RES_EQUIV_CALC::S2R], m_ESeries_Sol2R, m_ESeriesError2R );
+    showResult( m_eSeries.GetResults()[RES_EQUIV_CALC::S3R], m_ESeries_Sol3R, m_ESeriesError3R );
+    showResult( m_eSeries.GetResults()[RES_EQUIV_CALC::S4R], m_ESeries_Sol4R, m_ESeriesError4R );
 }
 
 
diff --git a/pcb_calculator/resistor_substitution_utils.cpp b/pcb_calculator/resistor_substitution_utils.cpp
index 6d3e25ec5b..540d93432a 100644
--- a/pcb_calculator/resistor_substitution_utils.cpp
+++ b/pcb_calculator/resistor_substitution_utils.cpp
@@ -2,7 +2,6 @@
  * This program source code file
  * is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2020 <janvi@veith.net>
  * Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
@@ -19,30 +18,167 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <cstdint>
-#include <string>
-#include <algorithm>
-#include <limits>
 #include "resistor_substitution_utils.h"
-#include "eseries.h"
+#include <algorithm>
+#include <cmath>
+#include <functional>
+#include <stdexcept>
 
-/*
- * If BENCHMARK is defined, any 4R E12 calculations will print its execution time to console
- * My Hasswell Enthusiast reports 225 mSec what are reproducible within plusminus 2 percent
- */
-//#define BENCHMARK
+// If BENCHMARK is defined, calculations will print their execution time to the STDERR
+// #define BENCHMARK
 
 #ifdef BENCHMARK
 #include <core/profile.h>
 #endif
 
+// Comparison operators used by std::sort and std::lower_bound
+bool operator<( const RESISTANCE& aLhs, double aRhs )
+{
+    return aLhs.value < aRhs;
+}
 
-// Return a string from aValue (aValue is expected in ohms)
-// If aValue < 1000 the returned string is aValue with unit = R
+bool operator<( const RESISTANCE& aLhs, const RESISTANCE& aRhs )
+{
+    return aLhs.value < aRhs.value;
+}
+
+class SolutionCollector
+/**
+ * Helper class that collects solutions and keeps one with the best deviation.
+ * In order to avoid performing costly string operations too frequently,
+ * they are postponed until the very end, when we know the best combination.
+ */
+{
+public:
+    SolutionCollector( double aTarget ) : m_target( aTarget ) {}
+
+    /**
+     * Add two solutions, based on single 2R buffer lookup, to the collector.
+     *
+     * @param aResults are the resistances found in 2R buffer
+     * @param aValueFunc transforms value from aResults into final value of the combination
+     * @param aResultFunc transforms RESISTANCE instance from aResults into final instance
+     */
+    void Add2RLookupResults( std::pair<RESISTANCE&, RESISTANCE&>      aResults,
+                             std::function<double( double )>          aValueFunc,
+                             std::function<RESISTANCE( RESISTANCE& )> aResultFunc )
+    {
+        addSolution( aValueFunc( aResults.first.value ), &aResults.first, aResultFunc );
+        addSolution( aValueFunc( aResults.second.value ), &aResults.second, aResultFunc );
+    }
+
+    /**
+     * Return the best collected combination, running the corresponding result_func.
+     */
+    RESISTANCE GetBest()
+    {
+        if( !m_best_found_resistance )
+            throw std::logic_error( "Empty solution collector" );
+
+        return m_best_result_func( *m_best_found_resistance );
+    }
+
+private:
+    /**
+     * Add single solution to the collector.
+     *
+     * @param aValue is a value of the combination in Ohms
+     * @param aFound is the corresponding RESISTANCE found in 2R buffer
+     * @param aResultFunc is a function calculating final result (RESISTANCE instance)
+     * for this combination
+     */
+    void addSolution( double aValue, RESISTANCE *aFound,
+                      std::function<RESISTANCE( RESISTANCE& )> aResultFunc )
+    {
+        double deviation = std::abs( aValue - m_target );
+        if( deviation < m_best_deviation )
+        {
+            m_best_deviation = deviation;
+            m_best_found_resistance = aFound;
+            m_best_result_func = aResultFunc;
+        }
+    }
+
+    double                                   m_target;
+    double                                   m_best_deviation = INFINITY;
+    RESISTANCE*                              m_best_found_resistance = nullptr;
+    std::function<RESISTANCE( RESISTANCE& )> m_best_result_func;
+};
+
+/**
+ * If aText contains aRequiredSymbol as top-level (i.e. not in parentheses) operator,
+ * return aText enclosed in parentheses.
+ * Otherwise, return aText unmodified.
+ */
+static std::string maybeEmbrace( const std::string& aText, char aRequiredSymbol )
+{
+    bool shouldEmbrace = false;
+
+    // scan for required top-level symbol
+    int parenLevel = 0;
+
+    for( char c : aText )
+    {
+        if( c == '(' )
+            parenLevel++;
+        else if( c == ')' )
+            parenLevel--;
+        else if( c == aRequiredSymbol && parenLevel == 0 )
+            shouldEmbrace = true;
+    }
+
+    // embrace or not
+    if( shouldEmbrace )
+        return '(' + aText + ')';
+    else
+        return aText;
+}
+
+/**
+ * Functions calculating values and text representations of serial and parallel combinations.
+ * Functions marked as 'Simple' do not care about parentheses, which makes them faster.
+ */
+
+static inline double serialValue( double aR1, double aR2 )
+{
+    return aR1 + aR2;
+}
+
+static inline double parallelValue( double aR1, double aR2 )
+{
+    return aR1 * aR2 / ( aR1 + aR2 );
+}
+
+static inline RESISTANCE serialResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
+{
+    std::string name = maybeEmbrace( aR1.name, '|' ) + " + " + maybeEmbrace( aR2.name, '|' );
+    return RESISTANCE( serialValue( aR1.value, aR2.value ), name );
+}
+
+static inline RESISTANCE parallelResistance( const RESISTANCE& aR1, const RESISTANCE& aR2 )
+{
+    std::string name = maybeEmbrace( aR1.name, '+' ) + " | " + maybeEmbrace( aR2.name, '+' );
+    return RESISTANCE( parallelValue( aR1.value, aR2.value ), name );
+}
+
+static inline RESISTANCE serialResistanceSimple( const RESISTANCE& aR1, const RESISTANCE& aR2 )
+{
+    std::string name = aR1.name + " + " + aR2.name;
+    return RESISTANCE( serialValue( aR1.value, aR2.value ), name );
+}
+
+static inline RESISTANCE parallelResistanceSimple( const RESISTANCE& aR1, const RESISTANCE& aR2 )
+{
+    std::string name = aR1.name + " | " + aR2.name;
+    return RESISTANCE( parallelValue( aR1.value, aR2.value ), name );
+}
+
+// Return a string from aValue (aValue is expected in ohms).
+// If aValue < 1000 the returned string is aValue with unit = R.
 // If aValue >= 1000 the returned string is aValue/1000 with unit = K
-// with notation similar to 2K2
+// with notation similar to 2K2.
 // If aValue >= 1e6 the returned string is aValue/1e6 with unit = M
-// with notation = 1M
+// with notation = 1M.
 static std::string strValue( double aValue )
 {
     std::string result;
@@ -73,7 +209,7 @@ static std::string strValue( double aValue )
         double mantissa = aValue - valueAsInt;
 
         if( mantissa > 0 )
-            result += std::to_string( static_cast<int>( ( mantissa * 10 ) + 0.5 ) );
+            result += std::to_string( lround( mantissa * 10 ) );
     }
 
     return result;
@@ -82,312 +218,262 @@ static std::string strValue( double aValue )
 
 RES_EQUIV_CALC::RES_EQUIV_CALC()
 {
-    // Build the list of available resistor values in each En serie
-    const ESERIES::ESERIES_VALUES listValuesE1 = ESERIES::E1_VALUES();
-    const ESERIES::ESERIES_VALUES listValuesE3 = ESERIES::E3_VALUES();
-    const ESERIES::ESERIES_VALUES listValuesE6 = ESERIES::E6_VALUES();
-    const ESERIES::ESERIES_VALUES listValuesE12 = ESERIES::E12_VALUES();
-    const ESERIES::ESERIES_VALUES listValuesE24 = ESERIES::E24_VALUES();
-    // buildSeriesData must be called in the order of En series, because
-    // the list of series is expected indexed by En for the serie En
-    buildSeriesData( listValuesE1 );
-    buildSeriesData( listValuesE3 );
-    buildSeriesData( listValuesE6 );
-    buildSeriesData( listValuesE12 );
-    int count = buildSeriesData( listValuesE24 );
-
-    // Reserve a buffer for intermediate calculations:
-    // the buffer size is 2*count*count to store all combinations of 2 values
-    // there are 2*count*count = 29282 combinations for E24
-    int bufsize = 2 * count * count;
-    m_combined_table.reserve( bufsize );
-
-    // Store predefined R_DATA items.
-    for( int ii = 0; ii < bufsize; ii++ )
-        m_combined_table.emplace_back( "", 0.0 );
+    // series must be added to vector in correct order
+    m_e_series.push_back( buildSeriesData( ESERIES::E1_VALUES() ) );
+    m_e_series.push_back( buildSeriesData( ESERIES::E3_VALUES() ) );
+    m_e_series.push_back( buildSeriesData( ESERIES::E6_VALUES() ) );
+    m_e_series.push_back( buildSeriesData( ESERIES::E12_VALUES() ) );
+    m_e_series.push_back( buildSeriesData( ESERIES::E24_VALUES() ) );
 }
 
-
-int RES_EQUIV_CALC::buildSeriesData( const ESERIES::ESERIES_VALUES aList )
+void RES_EQUIV_CALC::SetSeries( uint32_t aSeries )
 {
-    uint_fast32_t curr_decade = FIRST_VALUE;
-    int           count = 0;
-
-    std::vector<R_DATA> curr_list;
-
-    uint_fast16_t first_value_in_decade = aList[0];
-
-    for( ;; )
-    {
-        double curr_r = LAST_VALUE;
-
-        for( const uint16_t listvalue : aList )
-        {
-            curr_r = 1.0 * curr_decade * listvalue / first_value_in_decade;
-            curr_list.emplace_back( strValue( curr_r ), curr_r );
-            count++;
-
-            if( curr_r >= LAST_VALUE )
-                break;
-        }
-
-        if( curr_r >= LAST_VALUE )
-            break;
-
-        curr_decade *= 10;
-    }
-
-    m_tables.push_back( std::move( curr_list ) );
-
-    return count;
+    m_series = aSeries;
 }
 
+void RES_EQUIV_CALC::NewCalc( double aTargetValue )
+{
+    m_target = aTargetValue;
+
+    m_exclude_mask.resize( m_e_series[m_series].size() );
+    std::fill( m_exclude_mask.begin(), m_exclude_mask.end(), false );
+
+    std::fill( m_results.begin(), m_results.end(), std::nullopt );
+}
 
 void RES_EQUIV_CALC::Exclude( double aValue )
 {
-    if( aValue != 0.0 ) // if there is a value to exclude other than a wire jumper
-    {
-        for( R_DATA& i : m_tables[m_series] ) // then search it in the selected E-Series table
-        {
-            if( i.e_value == aValue )         // if the value to exclude is found
-                i.e_use = false;              // disable its use
-        }
-    }
+    if( std::isnan( aValue ) )
+        return;
+
+    std::vector<RESISTANCE>& series = m_e_series[m_series];
+    auto it = std::lower_bound( series.begin(), series.end(), aValue - epsilon );
+
+    if( it != series.end() && std::abs( it->value - aValue ) < epsilon )
+        m_exclude_mask[it - series.begin()] = true;
 }
 
-
-void RES_EQUIV_CALC::simple_solution( uint32_t aSize )
-{
-    uint32_t i;
-
-    m_results.at( S2R ).e_value =
-            std::numeric_limits<double>::max(); // assume no 2R solution or max deviation
-
-    for( i = 0; i < aSize; i++ )
-    {
-        if( std::abs( m_combined_table.at( i ).e_value - m_required_value )
-            < std::abs( m_results.at( S2R ).e_value ) )
-        {
-            m_results[S2R].e_value =
-                    m_combined_table[i].e_value - m_required_value; // save signed deviation in Ohms
-            m_results[S2R].e_name = m_combined_table[i].e_name;     // save combination text
-            m_results[S2R].e_use = true;                            // this is a possible solution
-        }
-    }
-}
-
-
-void RES_EQUIV_CALC::combine4( uint32_t aSize )
-{
-    uint32_t i, j;
-    double   tmp;
-
-    m_results[S4R].e_use = false;                    // disable 4R solution, until
-    m_results[S4R].e_value = m_results[S3R].e_value; // 4R becomes better than 3R solution
-
-#ifdef BENCHMARK
-    PROF_TIMER timer; // start timer to count execution time
-#endif
-
-    for( i = 0; i < aSize; i++ )     // 4R search outer loop
-    {                                // scan valid intermediate 2R solutions
-        for( j = 0; j < aSize; j++ ) // inner loop combines all with itself
-        {
-            tmp = m_combined_table[i].e_value
-                  + m_combined_table[j].e_value; // calculate 2R+2R serial
-            tmp -= m_required_value;             // calculate 4R deviation
-
-            if( std::abs( tmp ) < std::abs( m_results.at( S4R ).e_value ) ) // if new 4R is better
-            {
-                m_results[S4R].e_value = tmp;           // save amount of benefit
-                std::string s = "( ";
-                s.append( m_combined_table[i].e_name ); // mention 1st 2 component
-                s.append( " ) + ( " );                  // in series
-                s.append( m_combined_table[j].e_name ); // with 2nd 2 components
-                s.append( " )" );
-                m_results[S4R].e_name = s;              // save the result and
-                m_results[S4R].e_use = true;            // enable for later use
-            }
-
-            tmp = ( m_combined_table[i].e_value * m_combined_table[j].e_value )
-                  / ( m_combined_table[i].e_value
-                      + m_combined_table[j].e_value );                 // calculate 2R|2R parallel
-            tmp -= m_required_value;                                   // calculate 4R deviation
-
-            if( std::abs( tmp ) < std::abs( m_results[S4R].e_value ) ) // if new 4R is better
-            {
-                m_results[S4R].e_value = tmp;                          // save amount of benefit
-                std::string s = "( ";
-                s.append( m_combined_table[i].e_name );                // mention 1st 2 component
-                s.append( " ) | ( " );                                 // in parallel
-                s.append( m_combined_table[j].e_name );                // with 2nd 2 components
-                s.append( " )" );
-                m_results[S4R].e_name = s;                             // save the result
-                m_results[S4R].e_use = true;                           // enable later use
-            }
-        }
-    }
-
-#ifdef BENCHMARK
-    printf( "Calculation time = %d mS", timer.msecs() );
-    fflush( 0 );
-#endif
-}
-
-
-void RES_EQUIV_CALC::NewCalc()
-{
-    for( R_DATA& i : m_combined_table )
-        i.e_use = false; // before any calculation is done, assume that
-
-    for( R_DATA& i : m_results )
-        i.e_use = false; // no combinations and no results are available
-
-    for( R_DATA& i : m_tables[m_series] )
-        i.e_use = true; // all selected E-values available
-}
-
-
-uint32_t RES_EQUIV_CALC::combine2()
-{
-    uint32_t    combi2R = 0; // target index counts calculated 2R combinations
-    std::string s;
-
-    for( const R_DATA& i : m_tables[m_series] ) // outer loop to sweep selected source lookup table
-    {
-        if( i.e_use )
-        {
-            for( const R_DATA& j : m_tables[m_series] ) // inner loop to combine values with itself
-            {
-                if( j.e_use )
-                {
-                    m_combined_table[combi2R].e_use = true;
-                    m_combined_table[combi2R].e_value =
-                            i.e_value + j.e_value; // calculate 2R serial
-                    s = i.e_name;
-                    s.append( " + " );
-                    m_combined_table[combi2R].e_name = s.append( j.e_name );
-                    combi2R++;                              // next destination
-                    m_combined_table[combi2R].e_use = true; // calculate 2R parallel
-                    m_combined_table[combi2R].e_value =
-                            i.e_value * j.e_value / ( i.e_value + j.e_value );
-                    s = i.e_name;
-                    s.append( " | " );
-                    m_combined_table[combi2R].e_name = s.append( j.e_name );
-                    combi2R++; // next destination
-                }
-            }
-        }
-    }
-    return combi2R;
-}
-
-
-void RES_EQUIV_CALC::combine3( uint32_t aSize )
-{
-    uint32_t    j = 0;
-    double      tmp = 0; // avoid warning for being uninitialized
-    std::string s;
-
-    m_results[S3R].e_use = false;                    // disable 3R solution, until 3R
-    m_results[S3R].e_value = m_results[S2R].e_value; //  becomes better than 2R solution
-
-    for( const R_DATA& i : m_tables[m_series] ) // 3R  Outer loop to selected primary E series table
-    {
-        if( i.e_use )                           // skip all excluded values
-        {
-            for( j = 0; j < aSize; j++ )        // inner loop combines with all 2R intermediate
-            {                                   //  results R+2R serial combi
-                tmp = m_combined_table[j].e_value + i.e_value;
-                tmp -= m_required_value;        // calculate deviation
-
-                if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better
-                {                                                          // then take it
-                    s = i.e_name;                                          // mention 3rd component
-                    s.append( " + ( " );                                   // in series
-                    s.append( m_combined_table[j].e_name );                // with 2R combination
-                    s.append( " )" );
-                    m_results[S3R].e_name = s;                             // save S3R result
-                    m_results[S3R].e_value = tmp;                          // save amount of benefit
-                    m_results[S3R].e_use = true;                           // enable later use
-                }
-
-                tmp = i.e_value * m_combined_table[j].e_value
-                      / ( i.e_value + m_combined_table[j].e_value ); // calculate R + 2R parallel
-                tmp -= m_required_value;                             // calculate deviation
-
-                if( std::abs( tmp ) < std::abs( m_results[S3R].e_value ) ) // compare if better
-                {                                                          // then take it
-                    s = i.e_name;                                          // mention 3rd component
-                    s.append( " | ( " );                                   // in parallel
-                    s.append( m_combined_table[j].e_name );                // with 2R combination
-                    s.append( " )" );
-                    m_results[S3R].e_name = s;
-                    m_results[S3R].e_value = tmp; // save amount of benefit
-                    m_results[S3R].e_use = true;  // enable later use
-                }
-            }
-        }
-    }
-
-    // If there is a 3R result with remaining deviation consider to search a possibly better
-    // 4R solution
-    // calculate 4R for small series always
-    if( m_results[S3R].e_use && tmp )
-        combine4( aSize );
-}
-
-
 void RES_EQUIV_CALC::Calculate()
 {
-    uint32_t no_of_2Rcombi = 0;
+#ifdef BENCHMARK
+    PROF_TIMER timer( "Resistor calculation" );
+#endif
 
-    no_of_2Rcombi = combine2();       // combine all 2R combinations for selected E serie
-    simple_solution( no_of_2Rcombi ); // search for simple 2 component solution
+    prepare1RBuffer();
+    prepare2RBuffer();
 
-    if( m_results[S2R].e_value )      // if simple 2R result is not exact
-        combine3( no_of_2Rcombi );    // continiue searching for a possibly better solution
+    RESISTANCE solution_2r = calculate2RSolution();
+    m_results[S2R] = solution_2r;
 
-    strip3();
-    strip4();
+    if( std::abs( solution_2r.value - m_target ) > epsilon )
+    {
+        RESISTANCE solution_3r = calculate3RSolution();
+        m_results[S3R] = solution_3r;
+
+        if( std::abs( solution_3r.value - m_target ) > epsilon )
+            m_results[S4R] = calculate4RSolution();
+    }
+
+#ifdef BENCHMARK
+    timer.Show();
+#endif
 }
 
-
-void RES_EQUIV_CALC::strip3()
+std::vector<RESISTANCE> RES_EQUIV_CALC::buildSeriesData( const ESERIES::ESERIES_VALUES& aList )
 {
-    std::string s;
+    std::vector<RESISTANCE> result_list;
 
-    if( m_results[S3R].e_use ) // if there is a 3 term result available
-    {                          // what is connected either by two "|" or by 3 plus
-        s = m_results[S3R].e_name;
+    for( double curr_decade = RES_EQUIV_CALC_FIRST_VALUE;; curr_decade *= 10.0 ) // iterate over decades
+    {
+        double multiplier = curr_decade / aList[0];
 
-        if( ( std::count( s.begin(), s.end(), '+' ) == 2 )
-            || ( std::count( s.begin(), s.end(), '|' ) == 2 ) )
-        {                                 // then strip one pair of braces
-            s.erase( s.find( "( " ), 2 ); // it is known sure, this is available
-            s.erase( s.find( " )" ), 2 ); // in any unstripped 3R result term
-            m_results[S3R].e_name = s;    // use stripped result
+        for( const uint16_t listvalue : aList ) // iterate over values in decade
+        {
+            double value = multiplier * listvalue;
+            result_list.emplace_back( value, strValue( value ) );
+
+            if( value >= RES_EQUIV_CALC_LAST_VALUE )
+                return result_list;
         }
     }
 }
 
-
-void RES_EQUIV_CALC::strip4()
+void RES_EQUIV_CALC::prepare1RBuffer()
 {
-    std::string s;
+    std::vector<RESISTANCE>& series = m_e_series[m_series];
+    m_buffer_1R.clear();
 
-    if( m_results[S4R].e_use ) // if there is a 4 term result available
-    {                          // what are connected either by 3 "+" or by 3 "|"
-        s = m_results[S4R].e_name;
-
-        if( ( std::count( s.begin(), s.end(), '+' ) == 3 )
-            || ( std::count( s.begin(), s.end(), '|' ) == 3 ) )
-        {                                 // then strip two pair of braces
-            s.erase( s.find( "( " ), 2 ); // it is known sure, they are available
-            s.erase( s.find( " )" ), 2 ); // in any unstripped 4R result term
-            s.erase( s.find( "( " ), 2 );
-            s.erase( s.find( " )" ), 2 );
-            m_results[S4R].e_name = s; // use stripped result
-        }
+    for( size_t i = 0; i < series.size(); i++ )
+    {
+        if( !m_exclude_mask[i] )
+            m_buffer_1R.push_back( series[i] );
     }
 }
+
+void RES_EQUIV_CALC::prepare2RBuffer()
+{
+    m_buffer_2R.clear();
+
+    for( size_t i1 = 0; i1 < m_buffer_1R.size(); i1++ )
+    {
+        for( size_t i2 = i1; i2 < m_buffer_1R.size(); i2++ )
+        {
+            m_buffer_2R.push_back( serialResistanceSimple( m_buffer_1R[i1], m_buffer_1R[i2] ) );
+            m_buffer_2R.push_back( parallelResistanceSimple( m_buffer_1R[i1], m_buffer_1R[i2] ) );
+        }
+    }
+
+    std::sort( m_buffer_2R.begin(), m_buffer_2R.end() );
+}
+
+std::pair<RESISTANCE&, RESISTANCE&> RES_EQUIV_CALC::findIn2RBuffer( double aTarget )
+{
+    // in case of NaN, return anything valid
+    if( std::isnan( aTarget ) )
+        return { m_buffer_2R[0], m_buffer_2R[0] };
+
+    // target value is often too small or too big, so check that manually
+    if( aTarget <= m_buffer_2R.front().value || aTarget >= m_buffer_2R.back().value )
+        return { m_buffer_2R.front(), m_buffer_2R.back() };
+
+    auto it = std::lower_bound( m_buffer_2R.begin(), m_buffer_2R.end(), aTarget )
+              - m_buffer_2R.begin();
+
+    if( it == 0 )
+        return { m_buffer_2R[0], m_buffer_2R[0] };
+    else if( it == m_buffer_2R.size() )
+        return { m_buffer_2R[it - 1], m_buffer_2R[it - 1] };
+    else
+        return { m_buffer_2R[it - 1], m_buffer_2R[it] };
+}
+
+RESISTANCE RES_EQUIV_CALC::calculate2RSolution()
+{
+    SolutionCollector solution( m_target );
+
+    auto valueFunc = []( double aFoundValue )
+    {
+        return aFoundValue;
+    };
+    auto resultFunc = []( RESISTANCE& aFoundRes )
+    {
+        return aFoundRes;
+    };
+    solution.Add2RLookupResults( findIn2RBuffer( m_target ), valueFunc, resultFunc );
+
+    return solution.GetBest();
+}
+
+RESISTANCE RES_EQUIV_CALC::calculate3RSolution()
+{
+    SolutionCollector solution( m_target );
+
+    for( RESISTANCE& r : m_buffer_1R )
+    {
+        // try r + 2R combination
+        {
+            auto valueFunc = [&]( double aFoundValue )
+            {
+                return serialValue( aFoundValue, r.value );
+            };
+            auto resultFunc = [&]( RESISTANCE& aFoundRes )
+            {
+                return serialResistance( aFoundRes, r );
+            };
+            solution.Add2RLookupResults( findIn2RBuffer( m_target - r.value ), valueFunc,
+                                         resultFunc );
+        }
+
+        // try r | 2R combination
+        {
+            auto valueFunc = [&]( double aFoundValue )
+            {
+                return parallelValue( aFoundValue, r.value );
+            };
+            auto resultFunc = [&]( RESISTANCE& aFoundRes )
+            {
+                return parallelResistance( aFoundRes, r );
+            };
+            solution.Add2RLookupResults(
+                    findIn2RBuffer( m_target * r.value / ( r.value - m_target ) ), valueFunc,
+                    resultFunc );
+        }
+    }
+
+    return solution.GetBest();
+}
+
+RESISTANCE RES_EQUIV_CALC::calculate4RSolution()
+{
+    SolutionCollector solution( m_target );
+
+    for( RESISTANCE& rr : m_buffer_2R )
+    {
+        // try 2R + 2R combination
+        {
+            auto valueFunc = [&]( double aFoundValue )
+            {
+                return serialValue( aFoundValue, rr.value );
+            };
+            auto resultFunc = [&]( RESISTANCE& aFoundRes )
+            {
+                return serialResistance( aFoundRes, rr );
+            };
+            solution.Add2RLookupResults( findIn2RBuffer( m_target - rr.value ), valueFunc,
+                                         resultFunc );
+        }
+
+        // try 2R | 2R combination
+        {
+            auto valueFunc = [&]( double aFoundValue )
+            {
+                return parallelValue( aFoundValue, rr.value );
+            };
+            auto resultFunc = [&]( RESISTANCE& aFoundRes )
+            {
+                return parallelResistance( aFoundRes, rr );
+            };
+            solution.Add2RLookupResults(
+                    findIn2RBuffer( m_target * rr.value / ( rr.value - m_target ) ), valueFunc,
+                    resultFunc );
+        }
+    }
+
+    for( RESISTANCE& r1 : m_buffer_1R )
+    {
+        for( RESISTANCE& r2 : m_buffer_1R )
+        {
+            // try r1 + (r2 | 2R)
+            {
+                auto valueFunc = [&]( double aFoundValue )
+                {
+                    return serialValue( r1.value, parallelValue( r2.value, aFoundValue ) );
+                };
+                auto resultFunc = [&]( RESISTANCE& aFoundRes )
+                {
+                    return serialResistance( r1, parallelResistance( r2, aFoundRes ) );
+                };
+                solution.Add2RLookupResults( findIn2RBuffer( ( m_target - r1.value ) * r2.value
+                                                             / ( r1.value + r2.value - m_target ) ),
+                                             valueFunc, resultFunc );
+            }
+
+            // try r1 | (r2 + 2R)
+            {
+                auto valueFunc = [&]( double aFoundValue )
+                {
+                    return parallelValue( r1.value, serialValue( r2.value, aFoundValue ) );
+                };
+                auto resultFunc = [&]( RESISTANCE& aFoundRes )
+                {
+                    return parallelResistance( r1, serialResistance( r2, aFoundRes ) );
+                };
+                solution.Add2RLookupResults(
+                        findIn2RBuffer( m_target * r1.value / ( r1.value - m_target ) - r2.value ),
+                        valueFunc, resultFunc );
+            }
+        }
+    }
+
+    return solution.GetBest();
+}
diff --git a/pcb_calculator/resistor_substitution_utils.h b/pcb_calculator/resistor_substitution_utils.h
index 0240d64bb3..3095359904 100644
--- a/pcb_calculator/resistor_substitution_utils.h
+++ b/pcb_calculator/resistor_substitution_utils.h
@@ -20,168 +20,142 @@
 
 #pragma once
 
-#include <string>
-#include <cstdint>
-#include <vector>
-#include <array>
 #include "eseries.h"
+#include <array>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
 
-// First value of resistor in ohm
+const double epsilon = 1e-12; // machine epsilon for floating-point equality testing
+
+// First value of resistor in Ohm
+// It should be first value of the decade, i.e. power of 10
 // This value is only pertinent to the resistor calculator.
 // It is used to reduce the computational complexity of its calculations.
 // There are valid resistor values using E-series numbers below this
 // value and above the below LAST_VALUE.
-#define FIRST_VALUE 10
+#define RES_EQUIV_CALC_FIRST_VALUE 10
 
-// last value of resistor in ohm
+// Last value of resistor in Ohm
 // This value is only pertinent to the resistor calculator. See above.
-#define LAST_VALUE 1e6
+#define RES_EQUIV_CALC_LAST_VALUE 1e6
 
-// R_DATA handles a resistor: string value, value and allowed to use
-struct R_DATA
+// Struct representing resistance value together with its composition, e.g. {20.0, "10R + 10R"}
+struct RESISTANCE
 {
-    R_DATA() : e_use( true ), e_value( 0.0 ) {}
+    double      value;
+    std::string name;
 
-    R_DATA( const std::string& aName, double aValue )
+    RESISTANCE( double aValue = 0.0, std::string aName = "" ) :
+            value( aValue ), name( std::move( aName ) )
     {
-        e_use = true;
-        e_name = aName;
-        e_value = aValue;
     }
-
-    bool        e_use;
-    std::string e_name;
-    double      e_value;
 };
 
+
 class RES_EQUIV_CALC
-/*! \brief Performs calculations on E-series values primarily to find target values.
+/*! \brief Performs calculations on E-series values primarily to find target values
+ * as combinations (serial, parallel) of them.
  *
- * E_SERIES class stores and performs calcuations on E-series values. It currently
+ * RES_EQUIV_CALC class stores and performs calcuations on E-series values. It currently
  * is targeted toward the resistor calculator and hard codes some limitations
  * to optimize its use in the resistor calculator.
  *
- * At this time these limitations are that this class ignores all E-series larger
- * than E24 and it does not consider resistor values below 10 Ohm or above 1M Ohm.
+ * At this time these limitations are that this class handles only E-series up to
+ * E24 and it does not consider resistor values below 10 Ohm or above 1M Ohm.
  */
 {
 public:
     RES_EQUIV_CALC();
 
-    /**
-     * This calculator suggests solutions for 2R, 3R and 4R replacement combinations
-     */
     enum
     {
         S2R,
         S3R,
-        S4R
+        S4R,
+        NUMBER_OF_LEVELS
     };
 
+    /**
+     * Set E-series to be used in calculations.
+     * Correct values are from 0 to 4 inclusive,
+     * representing series (consecutively) E1, E3, E6, E12, E24.
+     * After changing the series, NewCalc must be called before running calculations.
+     */
+    void SetSeries( uint32_t aSeries );
+
+    /**
+     * Initialize next calculation, clear exclusion mask
+     * and erase results from previous calculation.
+     * 
+     * @param aTargetValue is the value (in Ohms) to be looked for
+     */
+    void NewCalc( double aTargetValue );
+
     /**
      * If any value of the selected E-series not available, it can be entered as an exclude value.
      *
-     * @param aValue is the value to exclude from calculation
-     * Values to exclude are set to false in the selected E-series source lookup table
+     * @param aValue is the value (in Ohms) to exclude from calculation
+     * Values to exclude are set to true in the current exclusion mask and will not be
+     * considered during calculations.
      */
     void Exclude( double aValue );
 
     /**
-     *  initialize next calculation and erase results from previous calculation
-     */
-    void NewCalc();
-
-    /**
-     * called on calculate button to execute all the 2R, 3R and 4R calculations
+     * Executes all the calculations.
+     * Results are to be retrieved using GetResults (below).
      */
     void Calculate();
 
     /**
-     * Interface for CheckBox, RadioButton, RequriedResistor and calculated Results
+     * Accessor to calculation results.
+     * Empty std::optional means that the exact value can be achieved using fewer resistors.
      */
-    void SetSeries( uint32_t aSeries ) { m_series = aSeries; }
-    void SetRequiredValue( double aValue ) { m_required_value = aValue; }
-
-    // Accessor:
-    const std::array<R_DATA, S4R + 1>& GetResults() { return m_results; }
+    const std::array<std::optional<RESISTANCE>, NUMBER_OF_LEVELS>& GetResults()
+    {
+        return m_results;
+    }
 
 private:
     /**
-     * Add values from aList to m_tables.  Covers all decades between FIRST_VALUE and LAST_VALUE.
-     * @return the count of items added to m_tables.
+     * Add values from aList to m_e_series tables.
+     * Covers all decades between FIRST_VALUE and LAST_VALUE.
      */
-    int buildSeriesData( const ESERIES::ESERIES_VALUES );
+    std::vector<RESISTANCE> buildSeriesData( const ESERIES::ESERIES_VALUES& aList );
 
     /**
-     * Build all 2R combinations from the selected E-series values
-     *
-     * Pre-calculated value combinations are saved in intermediate look up table m_combined_table
-     * @return is the number of found combinations what also depends from exclude values
-    */
-    uint32_t combine2();
+     * Build 1R buffer, which is selected E-series table with excluded values removed.
+     */
+    void prepare1RBuffer();
 
     /**
-     * Search for closest two component solution
-     *
-     * @param aSize is the number of valid 2R combinations in m_combined_table on where to search
-     * The 2R result with smallest deviation will be saved in results
-    */
-    void simple_solution( uint32_t aSize );
+     * Build 2R buffer, which consists of all possible combinations of two resistors
+     * from 1R buffer (serial and parallel), sorted by value.
+     */
+    void prepare2RBuffer();
 
     /**
-     * Check if there is a better 3 R solution than previous one using only two components.
-     *
-     * @param aSize gives the number of available combinations to be checked inside
-     *              m_combined_table.  Therefore m_combined_table is combined with the primary
-     *              E-series look up table.  The 3R result with smallest deviation will be saved
-     *              in results if better than 2R
+     * Find in 2R buffer two values nearest to the given value (one smaller and one larger).
+     * It always returns two valid values, even for input out of range or Nan.
      */
-    void combine3( uint32_t aSize );
+    std::pair<RESISTANCE&, RESISTANCE&> findIn2RBuffer( double aTargetValue );
 
     /**
-     * Check if there is a better four component solution.
-     *
-     * @param aSsize gives the number of 2R combinations to be checked inside m_combined_table
-     * Occupied calculation time depends from number of available E-series values with the power
-     * of 4 why execution for E12 is conditional with 4R check box for the case the previously
-     * found 3R solution is already exact
+     * Calculate the best combination consisting of exactly 2, 3 or 4 resistors.
      */
-    void combine4( uint32_t aSize );
-
-    /*
-     * Strip redundant braces from three component result
-     *
-     * Example: R1+(R2+R3) become R1+R2+R3
-     * and      R1|(R2|R3) become R1|R2|R3
-     * while    R1+(R2|R3) or (R1+R2)|R3) remains untouched
-     */
-    void strip3();
-
-    /*
-     * Strip redundant braces from four component result
-     *
-     * Example: (R1+R2)+(R3+R4) become R1+R2+R3+R4
-     * and      (R1|R2)|(R2|R3) become R1|R2|R3|R4
-     * while    (R1+R2)|(R3+R4) remains untouched
-     */
-    void strip4();
+    RESISTANCE calculate2RSolution();
+    RESISTANCE calculate3RSolution();
+    RESISTANCE calculate4RSolution();
 
 private:
-    std::vector<std::vector<R_DATA>> m_tables;
+    std::vector<std::vector<RESISTANCE>> m_e_series;
+    std::vector<bool>                    m_exclude_mask;
+    std::vector<RESISTANCE>              m_buffer_1R;
+    std::vector<RESISTANCE>              m_buffer_2R;
 
-    /* Note: intermediate calculations use m_combined_table
-     * if the biggest list is En, reserved array size should be 2*En*En of std::vector primary list.
-     * 2 component combinations including redundant swappable terms are for the moment
-     * ( using values between 10 ohms and 1Mohm )
-     * 72 combinations for E1
-     * 512 combinations for E3
-     * 1922 combinations for E6
-     * 7442 combinations for E12
-     * 29282 combinations for E24
-     */
-    std::vector<R_DATA> m_combined_table;               // intermediate 2R combinations
+    uint32_t m_series = ESERIES::E6;
+    double   m_target = 0;
 
-    std::array<R_DATA, S4R + 1> m_results;              // 2R, 3R and 4R results
-    uint32_t                    m_series = ESERIES::E6; // Radio Button State
-    double                      m_required_value = 0.0; // required Resistor
-};
+    std::array<std::optional<RESISTANCE>, NUMBER_OF_LEVELS> m_results;
+};
\ No newline at end of file