#!/usr/bin/perl -w

# Copyright 2012 Kevin Ryde

# This file is part of Math-NumSeq.
#
# Math-NumSeq is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Math-NumSeq is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Math-NumSeq.  If not, see <http://www.gnu.org/licenses/>.

use 5.010;
use strict;
use warnings;
use POSIX;
use List::Util 'max','min';

# uncomment this to run the ### lines
#use Smart::Comments;



{
  # ith()
  require Math::NumSeq::BalancedBinary;
  my $seq = Math::NumSeq::BalancedBinary->new;
  $seq->ith(4);
  foreach my $i (1 .. 30) {
    my $value = $seq->ith($i);
    printf "%2d %3d %12b\n", $i, $value, $value;
  }
  exit 0;
}

{
  # odd Catalan 2s

  require Math::NumSeq::Catalan;
  my $seq = Math::NumSeq::Catalan->new;

  # require Math::NumSeq::BalancedBinary;
  # my $seq = Math::NumSeq::BalancedBinary->new;

  require Math::NumSeq::DigitCountLow;
  my $low = Math::NumSeq::DigitCountLow->new (radix => 2, digit => 0);
  foreach (1 .. 40) {
    my ($i, $value) = $seq->next;
    my $low2s = $low->ith($value);
    my $oddvalue = $value / 2**$low2s;
    printf "%6b %s  %22s  %s\n", $i+1, $low2s, $oddvalue, $value;
  }
  exit 0;
}

{
  # value_to_i_estimate()

  # require Math::NumSeq::Catalan;
  # # my $seq = Math::NumSeq::Catalan->new;
  # my $seq = Math::NumSeq::Catalan->new (values_type => 'odd');

  require Math::NumSeq::BalancedBinary;
  my $seq = Math::NumSeq::BalancedBinary->new;

  my $target = 2;
  for (;;) {
    my ($i, $value) = $seq->next;
    if ($i >= $target) {
      $target *= 1.1;

      # require Math::BigRat;
      # $value = Math::BigRat->new($value);

      # require Math::BigFloat;
      # $value = Math::BigFloat->new($value);

      my $est_i = $seq->value_to_i_estimate($value);
      my $factor = $est_i / $i;
      printf "%d i_est=%d   v=%.10s  factor=%.3f\n",
        $i, $est_i, $value, $factor;
    }
  }
  exit 0;
}

{
  # value_to_i_floor()
  require Math::NumSeq::BalancedBinary;
  my $seq = Math::NumSeq::BalancedBinary->new;
  # {
  #   my $i = $seq->value_to_i(3);
  #   print "$i\n";
  # }
  my $input = 64;
  {
    my $i = $seq->value_to_i_floor($input);
    # $i = $seq->value_to_i_ceil($input);
    my $value = $seq->ith($i);
    printf "%d -> %d %d %#b\n", $input, $i, $value, $value;
  }
  # foreach my $i (1 .. 15) {
  #   my $value = $seq->ith($i);
  #   printf "%2d %3d %12b\n", $i, $value, $value;
  # }
  exit 0;
}

{
  # z,o

  my $uv_max = 0xFFFF_FFFF;
  $uv_max = ~0;
  my @num;
  $num[0][0] = 0;
  $num[1][1] = 1;
  OUTER: foreach my $z (1 .. 30) {
    my $num = $num[$z][0] = 1;  # all zeros, no ones
    foreach my $o (1 .. $z) {
      my $add = ($num[$z-1][$o] || 0);  # 0,... if $z>=1
      if ($num > $uv_max - $add) {
        delete $num[$z];
        last OUTER;
      }
      $num += $add;                     # 1,...
      $num[$z][$o] = $num;
    }
  }

  my $count = 0;
  foreach my $aref (@num) {
    next unless $aref;
    print join(' ',map {$count++; $_//'_'} @$aref),"\n";
  }
  print "count $count\n";
  exit 0;
}

{
  # table

  my @num;
  $num[0][0] = 1;
  foreach my $i (1 .. 10) {
    $num[$i][0] = 1;
    foreach my $j (1 .. $i) {
      $num[$i][$j]
        = $num[$i][$j-1]             # left
          + ($num[$i-1][$j] || 0);   # above
    }
  }

  foreach my $aref (@num) {
    next unless $aref;
    print join(' ',map {$_//'_'} @$aref),"\n";
  }
  exit 0;
}

{
  # excess

  my @num;
  $num[2][0] = 1;
  $num[1][1] = 1;

  sub num {
    my ($w, $e) = @_;
    ### num: "$w $e"
    die "$w,$e" if $e < 0;
    die "$w,$e" if $w < 1;
    die "$w,$e" if $w<$e;
    die "$w,$e" if ($w-$e) & 1;

    if (defined $num[$w][$e]) {
      return $num[$w][$e];
    }
    # 0, num(w-1,e-1)
    # 1, num(w-1,e+1)  if w-1 >= e+1, w>=e+2
    my $num = 0;
    if ($e > 0) {
      $num += num($w-1,$e-1);
    }
    if ($w >= $e+2) {
      $num += num($w-1,$e+1);
    }
    return ($num[$w][$e] = $num);
  }

  require Math::NumSeq::Catalan;
  my $seq = Math::NumSeq::Catalan->new;

  $seq->next;
  $seq->next;
  foreach (1 .. 15) {
    my ($i, $value) = $seq->next;
    my $num = num(2*$i,0);
    print "$i $value $num\n";
  }

  foreach my $aref (@num) {
    next unless $aref;
    print join(' ',map {$_//'_'} @$aref),"\n";
  }
  exit 0;
}
{
  # monotonic

  require Math::NumSeq::Catalan;
  # my $seq = Math::NumSeq::Catalan->new;
  my $seq = Math::NumSeq::Catalan->new (values_type => 'odd');

  # require Math::NumSeq::BalancedBinary;
  # my $seq = Math::NumSeq::BalancedBinary->new;

  my $prev = -1;
  foreach (1 .. 10000) {
    my ($i, $value) = $seq->next;
    if ($value < $prev) {
      die $i;
    }
    $prev = $value;
  }
  print "$prev\n";
  exit 0;
}

{
  # formula

  require Math::NumSeq::Catalan;
  my $seq = Math::NumSeq::Catalan->new;

  my $cumul = 0;
  foreach (1 .. 20) {
    my ($i, $value) = $seq->next;

    my $formula = 0;
    foreach my $k (1 .. $i-1) {
      $formula += $seq->ith($i-$k)*$k + $seq->ith($k)
    }
    print "$i value=$value formula=$formula\n";
    $cumul += $value;
  }
  exit 0;
}

{
  # cumulative

  require Math::NumSeq::Catalan;
   my $seq = Math::NumSeq::Catalan->new;

  my $prev = 0;
  my $cumul = 0;
  foreach (1 .. 20) {
    my ($i, $value) = $seq->next;
    $cumul += $value;
    my $diff = $cumul - $prev;
    print "$i value=$value cumul=$cumul diff=$diff\n";
    $prev = $cumul;
  }
  exit 0;
}

{
  # Catalan estimate

  require Math::Symbolic;
  my $tree = Math::Symbolic->parse_from_string('4^x / sqrt(3.14*x) / (x+1)');
  print "tree: $tree\n";

  require Math::Symbolic::Derivative;
  my $deriv = Math::Symbolic::Derivative::total_derivative($tree, 'x');
  print "deriv $deriv\n";
  $deriv = $deriv->simplify;
  print "deriv $deriv\n";
  exit 0;
}

{
  # by binary width
  require Math::NumSeq::BalancedBinary;
  my $seq = Math::NumSeq::BalancedBinary->new;

  my $count = 0;
  my $target = 4;
  for (;;) {
    my ($i, $value) = $seq->next;
    if ($value >= $target) {
      print "$target  $count\n";
      $count = 0;
      $target *= 4;
    }
    $count++;
  }
  exit 0;
}

{
  # value_to_i_estimate() 2^32
  # 2**32 i=36_714_788

  require Math::NumSeq::BalancedBinary;
  my $seq = Math::NumSeq::BalancedBinary->new;
  {
    my $est_i = $seq->value_to_i_estimate(2**32);
    print "2**32 i=$est_i\n";
  }
  {
    my $est_i = $seq->value_to_i_estimate(2**64);
    print "2**64 i=$est_i\n";
  }
  exit 0;
}

{
  # catalan estimate
  require Math::NumSeq::Catalan;
  my $seq = Math::NumSeq::Catalan->new;

  for (;;) {
    my ($i, $value) = $seq->next;
    my $est_value = catalan_estimate($i);
    print "$i $value $est_value\n";
  }
  exit 0;

  sub catalan_estimate {
    my ($n) = @_;
    return 4**$n / (sqrt(3.141592*($n||1)) * ($n+1));
  }
}
