#!/usr/bin/perl -w

# to make a voltage divider with four e6-series resistors and make it
# match a given ratio

use strict;
package Rdiv;
use Prefix;
use Eseries;
use Data::Dumper;

my @arr;
my @IX = (1, 1, 0, 0, 0, 1); # k top, k bot, rt1, rt2, rb1, rb2 (indexes)
my @k = (1, 0.1, 0.01);
my @save = @IX;
my $sq = 9.999;
my $st = 0;
my $dig = 1;

my $inf = 9.99e99;

##############################

my @value;
my @ivalue;
my @nam = qw/ ser par cb1 cb2 /;

sub val_init($$@) {
    my $min = shift;
    my $max = shift;
    my @serie = @_;

    @value = Eseries::ExpandLimit($min, $max, @serie);
    @ivalue = map { 1/$_ } @value;
}

sub val_show() {
    Eseries::print_arr(6, "Value", @value);
    Eseries::print_arr(6, "iValue", @ivalue);
}

sub val_find($) {
    my $val = shift;

    if ($val <= $value[      0]) { return 0; }       # in case $val < than all values in set
    if ($val >= $value[$#value]) { return $#value; } # in case $val > than all values in set

    for (my $ix = 0; $ix < @value; $ix++) {
	my $v = $value[$ix];
	if ($val == $v) { return $ix; }
	if ($val < $v) { return ($ix-1, $ix); }
    }

    return $#value;
}
sub val_ifind($) {
    my $val = shift;

    if ($val >= $ivalue[       0]) { return 0; }
    if ($val <= $ivalue[$#ivalue]) { return $#ivalue; }

    for (my $ix = 0; $ix < @ivalue; $ix++) {
	my $v = $ivalue[$ix];
	if ($val == $v) { return $ix; }
	if ($val > $v) { return ($ix-1, $ix); }
    }

    return $#ivalue;
}

sub r_sum(@) {
    my @vec = @_;
    my $val = 0;
    for (my $ix = 0; $ix < @vec; $ix++) { $val += $value[$vec[$ix]]; }
    $val;
}
sub r_err($$) {
    my $R = shift;
    my $r = shift;
    ($R - r_sum(@$r))/$R;
}
sub r_print(@) {
    my @vec = @_;
    my $val = 0;
    my $str = "";
    for (my $ix = 0; $ix < @vec; $ix++) {
	my $v = $value[$vec[$ix]];
	$val += $v;
	$str .= sprintf(" %g", $v);
    }
    $str .= sprintf(": %g", $val);
    $str;
}

sub r1($) {
    my $R = shift;
    my $sq = $inf;
    my @R = (0);
    my $type = 0;
    my $srt = 0;

    for my $r1 (@value) {
	my $rt;
	my $q;

	$rt = $r1;
	$q = abs(($R - $rt)/$R);
	if ($q < $sq) {
	    $sq = $q;
	    @R = ($r1);
	    $type = 0;
	    $srt = $rt;
	}
    }

    my $str = sprintf("%5.2f       %s: %s\n", 100*$sq, Prefix::pfx_en($srt,"", "%1s"), Prefix::pfx_en($R[0],"", "%-2s"));
    print $str;
    @R;
}
sub r2($) {
    # two resistors
    # brute force, quick enougth
    my $R = shift;
    my @sq = ($inf)x2;
    my @R = ( [0, 0] )x2;
    my @rt = (0)x2;

    for my $r1 (@value) {
	for my $r2 (@value) {
	    my $rt;
	    my $q;

	    # serial, type 0
	    $rt = $r1 + $r2;
	    $q = abs(($R - $rt)/$R);
	    if ($q < $sq[0]) {
		$sq[0] = $q;
		$R[0] = [ $r1, $r2 ];
		$rt[0] = $rt;
	    }

	    # parallell
	    $rt = 1/(1/$r1 + 1/$r2);
	    $q = abs(($R - $rt)/$R);
	    if ($q < $sq[1]) {
		$sq[1] = $q;
		$R[1] = [ $r1, $r2 ];
		$rt[1] = $rt;
	    }
	}
    }

    my @str;
    for (my $ix = 0; $ix < @sq; $ix++) {
	Prefix::encode_arr($R[$ix], "", "%-2s");
	$str[$ix] = sprintf("%5.2f  %3s  %s: %s%s\n", 100*$sq[$ix], $nam[$ix], Prefix::pfx_en($rt[$ix],"", "%1s"),
			    @{$R[$ix]} );
    }
    print @str;
    @R;
}
sub r3($) {
    # three resistors
    # brute force, quick enougth for E6 and E12, slow for E24 and up
    my $R = shift;
    my @sq = ($inf)x4;
    my @R = ( [0, 0] )x4;
    my @rt = (0)x4;

    for my $r1 (@value) {
	for my $r2 (@value) {
	    for my $r3 (@value) {
		my $rt;
		my $q;
		my $ix;

		# serial
		$ix = 0;
		$rt = $r1 + $r2 + $r3;
		$q = abs(($R - $rt)/$R);
		if ($q < $sq[$ix]) {
		    $sq[$ix] = $q;
		    $R[$ix] = [ $r1, $r2, $r3 ];
		    $rt[$ix] = $rt;
		}

		# parallell
		$ix = 1;
		$rt = 1/(1/$r1 + 1/$r2 + 1/$r3);
		$q = abs(($R - $rt)/$R);
		if ($q < $sq[$ix]) {
		    $sq[$ix] = $q;
		    $R[$ix] = [ $r1, $r2, $r3 ];
		    $rt[$ix] = $rt;
		}

		# combinded 1
		$ix = 2;
		$rt = 1/(1/$r1 + 1/($r2 + $r3));
		$q = abs(($R - $rt)/$R);
		if ($q < $sq[$ix]) {
		    $sq[$ix] = $q;
		    $R[$ix] = [ $r1, $r2, $r3 ];
		    $rt[$ix] = $rt;
		}

		# combinded 2
		$ix = 3;
		$rt = $r1 + 1/(1/$r2 + 1/$r3);
		$q = abs(($R - $rt)/$R);
		if ($q < $sq[$ix]) {
		    $sq[$ix] = $q;
		    $R[$ix] = [ $r1, $r2, $r3 ];
		    $rt[$ix] = $rt;
		}

	    }
	}
    }

    my @str;
    for (my $ix = 0; $ix < @sq; $ix++) {
	Prefix::encode_arr($R[$ix], "", "%-2s");
	$str[$ix] = sprintf("%5.2f  %3s  %s: %s%s%s\n", 100*$sq[$ix], $nam[$ix], Prefix::pfx_en($rt[$ix],"", "%1s"),
			    @{$R[$ix]} );
    }
    print @str;
    @R;
}

sub rfind($@) {
    my $opt = shift;
    my @arr = @_;

    my %opt = %$opt;
    if (!defined($opt{min})) { $opt{min} = 1;}
    if (!defined($opt{max})) { $opt{max} = 1e6;}
    if (!defined($opt{N}))   { $opt{N}   = 3;}
    if ($opt{N} < 1) { $opt{N} = 1; }

    my @E = Eseries::find($opt{E});
    #Eseries::print_arr(12, $opt{E}, @E);

    val_init($opt{min}, $opt{max}, @E);
    my $str;
    $str = Prefix::pfx_en($opt{min}, "Ohm");
    printf(" %-5s = %s\n", "min", $str);
    $str = Prefix::pfx_en($opt{max}, "Ohm");
    printf(" %-5s = %s\n", "max", $str);

    if ($opt{show}) {
	Eseries::print_arr(6, "E", @E);
	val_show();
    }

    Prefix::decode_arr(\@arr);
    print "CB1 = R1 || ( R2  + R3 )\n";
    print "CB2 = R1 +  ( R2 || R3 )\n";
    for (@arr) {
	my $val = $_;
	my $str = Prefix::pfx_en($val, "", "%1s");
	printf("%s:\n", $str);
	print("  err type     Rtot        R1        R2        R3\n");
	r1($_);
	if ($opt{N} > 1) { r2($_); }
	if ($opt{N} > 2) { r3($_); }
    }
}

##############################
# configuration A: two top and to bottom resistors

sub calc() {
    my $kt = $IX[0];
    my $kb = $IX[1];
    my $rt1 = $kt * $arr[$IX[2]];
    my $rt2 = $arr[$IX[3]];
    my $rb1 = $kb * $arr[$IX[4]];
    my $rb2 = $arr[$IX[5]];

    ($rt1+$rt2)/($rb1+$rb2);
}

sub run($) {
    my $R = shift;
    while ($R > 10) { $R /= 10; $dig *= 10; }
    while ($R <  1) { $R *= 10; $dig /= 10; }

    for my $kt (@k) {
	for my $kb (@k) {
	    for (my $ix2 = 0; $ix2 < @arr; $ix2++) {
		for (my $ix3 = 0; $ix3 < @arr; $ix3++) {
		    for (my $ix4 = 0; $ix4 < @arr; $ix4++) {
			@IX = ($kt, $kb, $ix2, $ix3, $ix4, 1);
			my $t = calc();
			my $q = abs(($R - $t)/$R);
			if ($q < $sq) {
			    @save = @IX;
			    $sq = $q;
			    $st = $t;
			}
		    }
		}
	    }
	}
    }
}

sub printR() {
    my $kt = $IX[0];
    my $kb = $IX[1];
    my $rt1 = $kt * $arr[$IX[2]];
    my $rt2 = $arr[$IX[3]];
    my $rb1 = $kb * $arr[$IX[4]];
    my $rb2 = $arr[$IX[5]];

    printf("%.3f %.3f %.3f %.3f\n", $rt1, $rt2, $rb1, $rb2);
}

sub res_div($) {
    my $R = shift;
    run($R);

    @IX = @save;
    my $kt = $IX[0];
    my $kb = $IX[1];
    my $rt1 = $kt * $arr[$IX[2]];
    my $rt2 = $arr[$IX[3]];
    my $rb1 = $kb * $arr[$IX[4]];
    my $rb2 = $arr[$IX[5]];

    ($dig * $rt1, $dig * $rt2, $rb1, $rb2, $dig * $st);
}

1;

__END__
