Skip to content

Instantly share code, notes, and snippets.

@jaggzh
Last active October 16, 2022 20:09
Show Gist options
  • Save jaggzh/f34385f3e9f964014ed75fcfcb5da6a7 to your computer and use it in GitHub Desktop.
Save jaggzh/f34385f3e9f964014ed75fcfcb5da6a7 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
use warnings;
use 5.24.0;
use Text::Table;
use Getopt::Lucid qw( :all );
# Colours!
my $a_match="\033[33;1m";
my $a_close="\033[36;1m";
my $a_rst="\033[;m";
# Defaults
my $def_maxi = 5;
my $def_maxm = $def_maxi * 25.4;
my $def_range = 2;
sub imp2d {
my $s = shift;
# say "";
# say " S: {$s}";
return int($s) if $s !~ /-/;
my ($whole, $f1, $f2) = ($s =~ m|^(.*)-(\d+)/(\d+)|);
my $frac = (int($f1)/int($f2));
my $res = int($whole)+$frac;
# say "Whole: $whole";
# say " f1: $f1";
# say " f2: $f2";
# say " Frac: $frac";
# say " Res: $res";
return $res;
}
sub gcd {
my ($a, $b) = @_;
($a,$b) = ($b,$a) if $a > $b;
while ($a) {
($a, $b) = ($b % $a, $a);
}
return $b;
}
sub is_float_or_int { $_[0] =~ /^(\d+(?:\.\d+)?)|\.\d+$/ }
sub is_int { $_[0] =~ /^\d+$/ }
sub def { defined shift }
sub main {
my @specs = (
Counter("verbose|v"),
Switch("2")->doc("1/2 increments"),
Switch("4")->doc("1/4 increments"),
Switch("8")->doc("1/8 increments (default)"),
Switch("16")->doc("1/16 increments"),
Switch("32|3")->doc("1/32 increments"),
Switch("64|6")->doc("1/64 increments"),
Param("range|r")->default($def_range)->
doc("Range of items around selected value to display (ex: --max=$def_range)")->
valid(\&is_int),
Param("maxi|max")->default($def_maxi)->
doc("Maximum (imperial) unit (ex: --max=3) (def: $def_maxi)")->
valid(\&is_float_or_int),
Param("maxm")->default($def_maxm)->
doc("Maximum (metric) unit (ex: --maxm=26) (def: $def_maxm) (not implemented)")->
valid(qr/(\d+(?:\.\d+)?)/),
#Param("config|C"),
#List("lib|l|I"),
#Keypair("define"),
Switch("help|h")
);
my $opt = Getopt::Lucid->getopt( \@specs )->validate;
if ($opt->get_help) {
say "Metric imperial table(s)";
print $opt->usage;
exit 0;
}
my $user_num;
if ($#ARGV+1 > 0) { # User gave an argument (or more?)
$user_num = shift @ARGV;
die "Provided target number ($user_num) is not a number"
if !is_float_or_int($user_num)
}
my $default_denom = 8;
my $st = 0;
my $en = $opt->get_maxi;
my $denom =
$opt->get_2 ? 2 :
$opt->get_4 ? 4 :
$opt->get_8 ? 8 :
$opt->get_16 ? 16 :
$opt->get_32 ? 32 :
$opt->get_64 ? 64 : $default_denom; # default
my $tb = Text::Table->new(
("Idx", "Metric(mm)", "Imperial", "Imperial(Dec)"));
die "No range to process (start = $st, end = $en)"
if $st > $en;
my @items;
my ($found_itemi_i, $found_itemi_exact_i);
my ($found_itemm_i, $found_itemm_exact_i);
# Main loop through inches. You're welcome.
for (my $in=$st; $in < $en; $in++) {
# Looping through incremental denominators
for my $num (1 .. $denom) {
my $div = gcd($num, $denom); # greatest common divisor
my $frac = $num/$denom;
my $mm = ($in + $frac)*25.4;
# say("$num / $denom (div: $div) ==> " . $num/$div . " / " . $denom/$div);
# say("In: $in");
my $cnum = $num/$div;
my $cdenom = $denom/$div;
my $imp_str;
if ($cnum == $cdenom) {
$imp_str = sprintf("%d", $in+1); # Reached, eg. 8/8 (so 0+8/8)
} else {
$imp_str = $in == 0 ?
sprintf("%d/%d", $cnum, $cdenom) :
sprintf("%d-%d/%d", $in, $cnum, $cdenom);
}
# my $imp_dec = sprintf("%f", $in + $num/$denom);
my $imp_dec = $in + $num/$denom;
# Early termination: Loop doesn't handle calculating the decimal
# so we'll just do it here.
last if $imp_dec > $opt->get_maxi;
# Keep values
push(@items, {mm=>$mm, imp=>$imp_str, dec=>$imp_dec});
if (defined $user_num) {
if (!defined $found_itemi_i) {
if ($user_num == $imp_dec) { # exact imperial match
$found_itemi_i = $#items;
$found_itemi_exact_i = $#items-1;
} elsif ($imp_dec > $user_num) {
$found_itemi_i = $#items;
$found_itemi_exact_i = $#items-1;
}
}
if (!defined $found_itemi_i) {
if ($user_num == $mm) { # exact metric match
$found_itemm_i = $#items;
$found_itemm_exact_i = $#items-1;
} elsif ($user_num < $mm && !defined $found_itemm_i) {
$found_itemm_i = $#items;
}
}
}
}
}
# Add values to table
if ( defined $user_num ) {
say "Found item imperial: " . ($found_itemi_i // '?');
say "Found item metric : " . ($found_itemm_i // '?');
if (!defined $found_itemi_i && !defined $found_itemm_i) {
die "Nothing found near the number you wanted. This is a bug, not your fault.";
}
}
for my $i (0 .. $#items) {
my $item = $items[$i];
my ($mm, $imp, $dec) = ($$item{mm}, $$item{imp}, $$item{dec});
my $addflag = 0;
if ( defined $user_num ) { # Results limited to user selection
if (defined $found_itemi_i) {
if ($item->{dec} == $user_num) {
$imp = "[$a_match$$item{imp}$a_rst]";
$dec = "[$a_match$$item{dec}$a_rst]";
$addflag = 1;
} elsif ($i >= $found_itemi_i - $opt->get_range &&
$i <= $found_itemi_i + $opt->get_range) {
$imp = "*$a_close$$item{imp}$a_rst";
$dec = "*$a_close$$item{dec}$a_rst";
$addflag = 1;
}
}
if (defined $found_itemm_i) {
if ($item->{mm} == $user_num) {
$mm = "[$a_match$$item{mm}$a_rst]";
$addflag = 1;
} elsif ($i >= $found_itemm_i - $opt->get_range &&
$i <= $found_itemm_i + $opt->get_range) {
$mm = "*$a_close$$item{mm}$a_rst";
$addflag = 1;
}
}
} else {
$addflag = 1;
}
if ($addflag) {
$tb->add($i, $mm, $imp, $dec);
}
}
# Print table
if (! -t *STDOUT) { print $tb; }
else { open my $pipe, "|-", "less -R"; $|=1; print $pipe $tb; }
}
main();
exit;
# my $tb = Text::Table->new(qw/Metric Imperial Imperial(Dec)/);
# for my $i (0 .. $#lines) {
# $tb->add($met[$i], $imp[$i]->{imp}, $imp[$i]->{dec});
# }
# open my $pipe, "|-", "less"; $|=1; print $pipe $tb;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment