#!/usr/bin/perl

use 5.042.2;
use utf8;

use bigint lib => 'GMP';                            # mejor explicar qué librería o método se usa
say "$0 bigint in effect\n" if bigint::in_effect; 

# combi is an idea i walked before, circa 1991, in c/Conde Guadalhorce, 2 - 7AB, 33206 Gijón, Spain 
# as combi(10,3) = 10!/((10-3)! *3!) = 10!/(7!*3!) = 

# convención: m sobre n  (m de mayor, n el número concreto)

# = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 / ((7 * 6 * 5 * 4 * 3 * 2) * ( 3 * 2))
# = 10 * 9 * 8 * (1) / ((1) * ( 3 * 2 )) 

# = 10 * 9 * 8 / ( 3 * 2 ) = PRODUCTORY FROM n TO M / n!                   # ESTÁ BIEN AUNQUE NO LO PAREZCA

# Formerly knows as combi(m,n) = m! / ((m-n)! * n!)
# 
# La fórmula correcta, para toda la humanidad
# España, Jesús Lozano Mosterín, Julio, 2026
# For all human kind
# En Gijón, a 6 de Julio de 2026
#
# lun 06 jul 2026 09:04:43 CEST
# lun 06 jul 2026 07:04:49 GMT
# Note: this code has paranormal problems 

# As a gift for NATO accords, Ankara, which begins 7 July 2026 
# As a gift for spanish and Great Britain and Europe royals  
# As a gift for Italia, and Ukranie
# As a gift for Russia
# As a gift for Israel and middle east
# As a gift for Japan, Korea, China, Vietman, Taiwan, Australia
# As a gift for Vietnam
# As a gift for Australia
# As a gift for Indian people
# As a gift for Latino América

# As a gift for female women of the world 

# use Memoize qw(memoize);
# memoize "factorial";
# memoize "productory";

no warnings 'recursion';

my $DEBUG = 1;

my $ITER = 10_000;

my ($M, $n);
($M, $n) = ($ARGV[0],$ARGV[1]) if @ARGV;
$M //= 1;                                                    # debería ser
$n //= 1;                                                    # podría no ser

usage() unless (isan_intnum($n) + isan_intnum($M) != 24);     # aquí estoy reinventando cosas que igual no necesito
usage() unless ($M > $n && $n > 1);

say "m = $M\nn = $n\n" if $DEBUG;

my $t0 = time();

main:  {
    
    say "Método ANTIGUO   = ", factorial($M) / (factorial($M-$n)*factorial($n));
    say "";    
    say "Método PROPUESTO = ", productory($M, $M-$n+1) / factorial($n);    # De M-n a M es el orden natural
    
#    goto fin;
}

say "\nBegin comparison test times\n";

# use Benchmark qw ( cmpthese );                      # si instalo el GMP no puedo mirar esto

#  my $contador = 1;

# cmpthese (-1, {
# 		  "old" => 'old()',
#		  "current" => 'currrent();',
#	      }
# );

my $r = 0;
my $t1 = time();
for (1 .. $ITER){
    $r = factorial($M) / (factorial($M-$n) * factorial($n));    # método VIEJO,CONVENCIONAL
}
my $t2 = time();
for (1 .. $ITER){
    $r = productory($M-$n+1, $M) / factorial($n);               # método NUEVO, JLM
}
my $t3 = time();

say "Método VIEJO, CONVENCIONAL   = ", $t2 - $t1, " segundos";
say "Método NUEVO, JLM, PROPUESTO = ", $t3 - $t2, " segundos";

fin:

say "$0 at ", scalar localtime(time()), "   elapsed ", (time()-$t0) / 60 , " minutes\n";

exit 2;

sub old {                               # fórmula normal, clásica
    my $o = shift;
    for ( 1 .. 100 ) {
	$o = factorial($M) / ( factorial( $M-$n ) * factorial( $n ) );
    }
    return $o;
}

sub current {                              # fórmula propuesta JLM
    my $r = shift;
    for  ( 1 .. 100 ) { 
	$r = productory( $M-$n+1, $M ) / factorial( $n ); 
    } 
    return $r;
}

sub usage {
    say "Error: usage $0 <m> <n>";
    say "but n and/or m are NOT integer positive numbers";
    say "and/or m greater than or equal n";
    say "and/or n > 1";
    say "Sorry about your type :-)\n";
}

sub isan_intnum {
    my $i = shift;
    return 0 unless ( $i =~ /\d/ );
#    if ( 0+$i eq $i ){            # esto sería un número normal 
#	$flag *= 4; 
#    }
    return 0 if ( $i =~ /\D/ );               # return 1 es false
    return 1;                             # 2 if a vaid code for 2 numbers                      
}

sub productory {                    #  from n to m     =>   n < m
    my ($n1, $M1) = @_;
    if ($M1 < $n1){
	($n1, $M1) = ($M1, $n1);
    }
    my $p1 = 1;
    for my $k1 ($n1 .. $M1){
	$p1 *= $k1;
    }
    return $p1;
}

sub factorial {
    my $i = shift;
    return 1 if $i <= 1;
    return $i * factorial( $i-1 );
}

# sub factorial {
#    my $y = shift;
#    return if $y == 1;          /* este error tiene interés pq de salir bien pasa a dar 1 */
#    return $y * factorial($y-1);
# }


__END__
  Creo que mi método con la función productorio(n,m) es mejor,
  porque independientemente de la velocidad, hace menos recursiones.
  Por así decirlo, es una subrutina más «desenrrollada» que una
  fórmula con nada menos que tres factoriales. Aquí hay un productorio
  y un solo factorial.
  
Jesús Lozano Mosterín
  jue 09 jul 2026 08:22:55 CEST
  jue 09 jul 2026 06:23:00 GMT
---
version alpha, menos equivocaciones  
jue 16 jul 2026 07:43:29 CEST
jue 16 jul 2026 05:43:35 GMT

Se ha comprobado en tiempo mejor, para números medianos. Del orden de menos
  de la mitad de tiempo. Posibles usos: generar llaves de números consecutivos,
  en poco tiempo.
  

