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.