Integer Factorization: The Latest quickfact.pl Script

Advertisements

Nothing earthshaking or historic is here. This is just my latest and greatest attempt to create a Perl script that improves on Fermat factorization, with the simplest and shortest code possible.

As always, if anyone wants to try this algorithm out in a language, or an extension of Perl, that can use arbitrary-precision integers, it would be interesting to see how it behaves with products of very large primes.

The code follows:

#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw/ceil/;

# Name: quickfact.pl
# Author: J. Calvin Smith
#
# Notice: Copyright 2023 J. Calvin Smith <jcsbimp@me.com>
#         <https://jcsbimp.com> Ceiling-Square-based
#         integer factorization script and algorithm.
#
#         Licensed for use under terms of GNU-GPL v3,
#         publicly available at the web link:
#         https://opensource.org/license/gpl-3-0/, as
#         follows:
#
# This program 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 of the License, or (at your option) any later version.
# This program 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 (linked above) for more details.
#
# --------
#
# Revision History:
#   28 September 2023 - Initial Release; put on jcsbimp.com blog
#   26 October 2023 - Improvement using intersection points
# 
# --------
#
# Usage: quickfact.pl <pos_int>
#
#    pos_int - the integer the user wishes to factor
#
# This Perl script finds a positive integer factor of pos_int,
# finding a non-trivial factor, if possible.
# It uses a method derived from an analytic geometry construction
# based on boundary lines of the integer grid points of f(x,y) =
# x(x+1)+y, using straight lines on the grid thus numbered between
# consecutive perfect squares to create "zone boundaries." It bases
# calculations on the first two elements of the series of remainders
# obtained by finding the Ceiling Square c^2 of pos_int, the least
# perfect square greater than pos_int that is even or odd as
# ( pos_int + 1 ) / 2 is even or odd, and computing c^2 - pos_int
# and (c+1)^2 - pos_int. Calculations of a "remainder line" that
# intersects the zone boundaries lead the algorithm to a solution
# when the points of intersection have integer x values.
#
# NOTES FOR USE: Integers here are of the standard size in Perl.
# No use of arbitrary-precision variables or arithmetic appears,
# but a developer wishing to make a future such version to test the
# algorithm on arbitrarily large integers has both the author's permis-
# sion and his blessing, this being free and proof-of-concept  software.

# Test for argument vector; give usage line if empty.

my $num_args = $#ARGV + 1;

if ( $num_args == 0 ) {
  print "Usage: quickfact.pl <pos_int>\n",
        "  pos_int = positive integer to factor\n";
  exit( 0 );
}
my $pos_int = $ARGV[0];
if ( $pos_int < 1 ) {
  print "ERROR: $pos_int is less than 1. Quitting.\n";
  exit( 0 );
}
my $pos_ceil = ceil( $pos_int );
if ( $pos_int != $pos_ceil ) {
  print "ERROR: $pos_int is not an integer. Quitting.\n";
  exit( 0 );
}

# Declare variables.

open my $out_fh, '>', "outfile_$pos_int.csv" or die $!;

my $c; # the (Adjusted) Ceiling Root of m
my $r0; # c^2-m, the first remainder
my $csr0; # Ceiling of sqrt of the remainder

my ( $meo, $ceo ); # Even/odd tests to set Ceiling Root
my ( $numer, $numinc, $denom, $the_mod ); # For the test fraction
my $the_gcd; # for the secondary test
my $num_iters = 0; # The number of iterations of the main loop
my $not_done_yet = 1; # Loop control variable
my ( $s, $t ); # the greater and lesser squares
my $succstr = 'SUCCESS';
my $primstr = 'PRIME';

# Determine if pos_int is even or odd.

if ( ( $pos_int % 2 ) == 0 ) {
  print "SUCCESS: $pos_int = 2 * ", $pos_int/2, ".\n";
  exit( 0 );
}

# pos_int is an odd positive integer.

# Calculate Ceiling Root of pos_int.

$c = ceil( sqrt( $pos_int ) );
$ceo = $c % 2;
$meo = ( ( $pos_int + 1 ) / 2 ) % 2;
if ( $ceo != $meo ) { # if unequal, make equal
  $c++;               #   by adjusting Ceiling Root
}

# Calculate the first term of the Remainder Series of pos_int.

$r0 = $c * $c - $pos_int;

# Check this remainder for easy Fermat factorization.

$csr0 = ceil( sqrt( $r0 ) );
if ( ( $csr0 * $csr0 ) == $r0 ) { # Easy answer: r0 is a perfect square.
  print "SUCCESS: $pos_int = ", ( $c - $csr0 ), " * ", ( $c + $csr0 );
  print ",\n  found by Fermat factorization on Remainder 0.\n";
  exit( 0 );
}

# Now ready to execute the main loop. Set up initial values.

$numer = 0 - $r0;
$denom = 2 * $c;
if ( ( $numer % 2 ) == 1 ) {
  $numer++;
  $denom -= 2;
  $numinc = 8;
}
else {
  $numinc = 4;
}

# The Main Loop

while ( $not_done_yet ) {
  $the_mod = $numer % $denom;
  if ( $the_mod == 0 ) { # Success!
    $not_done_yet = 0;
    $s = $c + $numer / $denom;
    $t = sqrt( $s * $s - $pos_int );
    if ( ( $s - $t ) == 1 ) {
      print $primstr;
    }
    else {
      print $succstr;
    }
    print ": $pos_int = ";
    print ( $s - $t );
    print " * ";
    print ( $s + $t );
    print ".\nFound on iteration $num_iters.\n";
  }
  else {
    # Now check for a gcd match.
    $the_gcd = gcd( $numer, $denom );
    if ( $the_gcd > 2 ) {
      $the_gcd = gcd( $the_gcd, $pos_int );
      if ( $the_gcd > 2 ) {
        print "SUCCESS: $pos_int = $the_gcd * ";
        print ( $pos_int / $the_gcd );
        print ".\nFound on iteration $num_iters.\n";
        $not_done_yet = 0;
      }
    }
  }
  $numer += $numinc;
  $numinc += 8;
  $denom -= 4;
  if ( $denom <= 0 ) {
    if ( $not_done_yet ) {
      print "No factorization found. Sorry!\n";
      print "  Especially since this isn't supposed to happen!\n";
    }
  }  
  $num_iters++;
}

sub gcd {
  my ($u, $v) = @_;
  if ($v) {
    return gcd($v, $u % $v);
  } else {
    return abs($u);
  }
}

Leave a ReplyCancel reply