This is my response to the Perl Weekly Challenge #066.
$M and $N.
$M / $N without
using multiplication, division and mod operator and return the floor of the result
of the division.
Input: $M = 5, $N = 2
Output: 2
Example 2
Input: $M = -5, $N = 2
Output: -3
Example 3
Input: $M = -5, $N = -2
Output: 2
Division is just subtraction in a loop. Off we go. Off we go. Off we go...:
File: divint
#! /usr/bin/env raku
unit sub MAIN (Int $M, Int $N where $N != 0, :$verbose); # [1]
my $count = 0; # [2]
my $negate = $M.sign != $N.sign; # [3]
my $M-abs = $M.abs; # [4]
my $N-abs = $N.abs; # [4]
while $M-abs >= $N-abs # [5]
{
$M-abs -= $N-abs; # [6]
$count++; # [7]
say "M: $M-abs N: $N-abs = $count (negate $negate)" if $verbose;
}
if $negate # [8]
{
$count = -$count; # [9]
$count-- if $M-abs; # [10]
}
say $count; # [2]
We have to take care of two things; the sign
and any remainder (but only if the sign is negative).
[1] Divison by zero is a bad thing, so we prevent that possibility for $N.
[2] The result.
[3] The result is a negative integer if one and only one of $M
and $M is neagtive, else it is positive. We use sign to get the
sign (-1, 0 or 1).
[4] Are we counting down to - or up to - zero? That
depends on the sign. But we can count down if we ensure that both values are positive.
That is done with abs, that removes any negative sign. The sign of the
result has been taken care of in [3].
[5] As long as the remainder is larger or equal to the divisor,
[6] • subtract the divisor.
[7] • increase the count, which is the end result.
[8] Should the answer be negative?
[9] • negate it.
[10] • subtract 1 (the «floor» part of the challenge description) if we have a remainder.
See
docs.raku.org/routine/sign for more
information about the sign method.
See
docs.raku.org/routine/abs for more
information about the abs method.
Running it:
$ raku divint 5 2
2
$ raku divint -5 2
-3
$ raku divint -5 -2
2
$ raku divint 5 -2
-3
With verbose mode (which would benefit from a rethink, me think...):
$ raku divint --verbose 5 2
: Negate the result: No
: M: 3 N: 2 = 1
: M: 1 N: 2 = 2
2
$ raku divint --verbose 25 2
: Negate the result: No
:M: 23 N: 2 = 1
:M: 21 N: 2 = 2
:M: 19 N: 2 = 3
:M: 17 N: 2 = 4
:M: 15 N: 2 = 5
:M: 13 N: 2 = 6
:M: 11 N: 2 = 7
:M: 9 N: 2 = 8
:M: 7 N: 2 = 9
:M: 5 N: 2 = 10
:M: 3 N: 2 = 11
:M: 1 N: 2 = 12
12
Perl does not have a «sign» function, so I wrote one. I chose to do command line handling manually. Other than that, the code is pretty much the same as the raku version. Except longer...
File: divint-perl
#! /usr/bin/env perl
use feature 'say';
use feature 'signatures';
no warnings qw(experimental::signatures);
my $verbose = 0;
if (@ARGV && $ARGV[0] eq "--verbose")
{
$verbose++;
shift @ARGV;
}
my $M = shift(@ARGV) || die 'Specify $M and $N';
my $N = shift(@ARGV) || die 'Specify $N';
die '$M is not an integer' unless int($M) == $M;
die '$N is not an integer' unless int($N) == $N;
die "Unable to divide by zero" if $N == 0;
sub sign ($value)
{
return 1 if $value > 0;
return 0 if $value == 0;
return -1 if $value < 0;
}
my $count = 0;
my $negate = sign($M) != sign($N);
my $M_abs = abs($M);
my $N_abs = abs($N);
say ": Negate the result: ", $negate ? 'Yes' : 'No' if $verbose;
while ($M_abs >= $N_abs)
{
$M_abs -= $N_abs;
$count++;
say "M: $M_abs N: $N_abs = $count" if $verbose;
}
if ($negate)
{
$count = -$count;
$count-- if $M_abs;
}
say $count;
$N.
Write a script to check if the given number can be expressed as mn where
m and n are positive integers. Otherwise print 0.
m > 1 and n > 1.
$N = 9, it should print 32 or 3^2.
$N = 45, it should print 0.
$N = 64, it should print all or one of 8^2 or 2^6
or 4^3.
#! /usr/bin/env raku
multi sub MAIN (Int $N where $N.is-prime, :$one) # [1] [2]
{
say 0;
}
multi sub MAIN (Int $N where $N > 1, :$one) # [3] [2]
{
my $match = 0; # [4]
for 2 .. $N.sqrt -> $base # [5]
{
for 2 .. Inf -> $exp # [6]
{
my $candidate = $base ** $exp; # [7]
last if $candidate > $N; # [8]
(say "$base^$exp"; $match++; exit if $one) if $candidate == $N; # [9]
}
}
say 0 unless $match; # [10]
}
[1] Prime numbers cannot be factoralized, so we can check for that (with
is-prime) up front. This speeds up the program in case of prime numbers,
but isn' strictly necessary
[2] Use the «--one» command line option if you only want one result.
[3] The where clause ensures that the value is as requested by the
challenge.
[4] Do we have a match (or more), so that we can give the «0» error message in [10].
[5] Iterate over the base number, from 2 to the square root of the original number.
This will stop our loop when we have reached the largest number (for $base)
that we can fit (in $N) by squaring it.
[6] The exponent, as an infinite loop.
[7] A possible answer?
[8] If the possible answer is higher than the initial value, we can abort the inner (infinite) loop.
[9] We have a match, print it, count it, and exit if we only wanted one result. Note
the funny use of parens (the grouping operator) to stack more than one expressions into
a postfix if. Brackets are also allowed, instead of the parens.
[10] No match?, say so.
See
docs.raku.org/routine/is-prime for more information about is-prime.
See
docs.raku.org/routine/** for more
information about the exponentiation operator **.
Running it:
$ raku powint 9
3^2
$ raku powint 45
0
$ raku powint 64
2^6
4^3
8^2
$ raku powint --one 64
2^6
#! /usr/bin/env perl
use feature 'say';
my $one = 0;
if (@ARGV && $ARGV[0] eq "--one")
{
$one++;
shift @ARGV;
}
my $N = shift(@ARGV) || die 'Specify $N';
die '$N is not an integer' unless int($N) == $N;
die '$N must be an integer larger than 1' unless $N > 1;
my $match = 0;
for my $base (2 .. sqrt($N))
{
my $exp = 1;
while ($exp++)
{
my $candidate = $base ** $exp;
last if $candidate > $N;
if ($candidate == $N)
{
say "$base^$exp";
$match++;
exit if $one;
}
}
}
say 0 unless $match;
And that's it.