A Fun Perfect Square Checker Using Integer Arithmetic Only... ;o)

A recent Python upload on another site gave me the inspiration to do an unusual bash version...

This is a little tongue-in-cheek but an enjoyable bit of fun.

It took around 11 seconds to prove 90000000000 had a perfect square of 300000...

It is a stand alone program and has a degree of INPUT error correction...

It was done on a MacBook Pro, OSX 10.7.5, default bash terminal and should work on Linux and UNIX flavours but it is untested...

Enjoy finding simple solutions to often very difficult problems...

Bazza...

#!/bin/bash
# perfect_square <number>
number=$1
if [ "$number" -eq "$number" ] > /dev/null 2>&1 
then
	if [ $number -lt 0 ]
	then
		echo "Warning! Integer is negative!!!"
		echo "Set input integer to the DEMO value of 99..."
		number=99
	fi
else
	echo "Invalid Argument! Set input integer to the DEMO value of 100..."
	number=100
fi
series=1
square=1
root=1
while true
do
	if [ $square -le $number ]
	then
		if [ $square -eq $number ]
		then
			echo "$number is the perfect square of $root..."
			exit 0
		fi
		root=$((root+1))
		series=$((series+2))
		square=$((square+series))
	else
		echo "Integer $number is not a perfect square..."
		exit 1
	fi
done
exit 0
# Last login: Tue Sep 16 20:12:27 on ttys000
# AMIGA:barrywalker~> chmod 755 perfect_square
# AMIGA:barrywalker~> ./perfect_square ierooeirt
# Invalid Argument! Set input integer to the DEMO value of 100...
# 100 is the perfect square of 10...
# AMIGA:barrywalker~> ./perfect_square -345
# Warning! Integer is negative!!!
# Set input integer to the DEMO value of 99...
# Integer 99 is not a perfect square...
# AMIGA:barrywalker~> ./perfect_square 0
# Integer 0 is not a perfect square...
# AMIGA:barrywalker~> ./perfect_square 123.9
# Invalid Argument! Set input integer to the DEMO value of 100...
# 100 is the perfect square of 10...
# AMIGA:barrywalker~> ./perfect_square 625
# 625 is the perfect square of 25...
# AMIGA:barrywalker~> ./perfect_square 1
# 1 is the perfect square of 1...
# AMIGA:barrywalker~> ./perfect_square 11111
# Integer 11111 is not a perfect square...
# AMIGA:barrywalker~> ./perfect_square oiwero11234ldkf
# Invalid Argument! Set input integer to the DEMO value of 100...
# 100 is the perfect square of 10...
# AMIGA:barrywalker~> ./perfect_square -0.0
# Invalid Argument! Set input integer to the DEMO value of 100...
# 100 is the perfect square of 10...
# AMIGA:barrywalker~> ./perfect_square -1.25
# Invalid Argument! Set input integer to the DEMO value of 100...
# 100 is the perfect square of 10...
# AMIGA:barrywalker~> ./perfect_square -1
# Warning! Integer is negative!!!
# Set input integer to the DEMO value of 99...
# Integer 99 is not a perfect square...
# AMIGA:barrywalker~> _
1 Like

On the same system, using ksh instead of bash , try:

#!/bin/ksh
# perfect_square <number>
number=$1
if [ "$number" -eq "$((int(number)))" ] > /dev/null 2>&1 
then
	if [ "$number" -lt 0 ]
	then
		echo "Warning! Integer is negative!!!"
		echo "Set input integer to the DEMO value of 99..."
		number=99
	fi
else
	echo "Invalid Argument! Set input integer to the DEMO value of 100..."
	number=100
fi
root=$((int(number ** .5)))
square=$((root ** 2))
if [ $square -eq $number ]
then
	echo "$number is the perfect square of $root..."
	exit 0
else
	echo "Integer $number is not a perfect square..."
	exit 1
fi

PS Timing the script:

time perfect_square 90000000000
90000000000 is the perfect square of 300000...

real	0m0.00s
user	0m0.00s
sys	0m0.01s

Unfortunately, however, this only works with a 1993 or later version of ksh .

1 Like

Looks quite nice.

I think you can simplify that a tiny bit from while true ; if [ $square -le $number ] into while [ $square -lt $number ] .

1 Like

Don cheated!!

Using same method, different interpreter:

mute@tiny:~$ time gawk -f ./square1 <<< $'90000000000\n4\n1'
90000000000 is the perfect square of 300000
4 is the perfect square of 2
1 is the perfect square of 1

real    0m0.081s
user    0m0.063s
sys     0m0.016s
{
  number=$1
  series=1
  square=1
  root=1
  while (square <= number) {
    if (square == number) {
      printf("%d is the perfect square of %d\n", number, root)
      next
    }
    root++
    series+=2
    square+=series
  }
  print "Not a perfect square"
  next
}

---------- Post updated at 10:58 PM ---------- Previous update was at 10:45 PM ----------

using a binary search was idea taken from friend.. they implemented in bash and I again convert to awk. using "one true awk" is a tad faster than gawk

mute@tiny:~$ time bawk -f ./square2 90000000000
90000000000 is a perfect square of 300000

real    0m0.002s
user    0m0.001s
sys     0m0.001s
#!/usr/bin/awk -f
BEGIN {
  num = ARGV[1]
  i = 10
  while (i * i < num)
    i *= 10

  lower = i/10
  upper = i

  while ( (avg = (upper + lower) / 2) != lower )
    if (avg * avg > num)
      upper = avg
    else
      lower = avg

  if (avg * avg == num)
    print num" is a perfect square of "avg
  else
    print num" is not a perfect square"

  exit
}
2 Likes

Let me get this straight... I use a different algorithm in a ksh script and get better times than using the original algorithm in a bash script, and I'm cheating...

But neutronscott uses a different algorithm in awk to get times similar to my ksh script and that's OK???

Sounds like a different standard of fairness to me. :wink:

EDIT: :doh: nevermind to much of math/conditition issues here atm, this is just one of those times i wish one could delete one owns post as long noone else has replied to the thread.

Fellas, fellas, settle down... ;oD

I seem to have created a riot on this thread; now let me mediate.

Firstly I did quote using "INTEGER arithmetic".

Secondly I also quoted "it is a little tongue-in-cheek".

And thirdly it uses builtins only...

At Don...
Don, in his defence, used builtins although not INTEGER arithmetic; he admitted that ksh93 was used as he expended on my original upload using ksh's floating point - root=$((int(number ** .5))) ; I am assuming 'int' is part of ksh's arithmetic...
So Don, as bash cannot do floating point then you did cheat... ;oD

At neutronscott...
Well what can I say... ;oD
Using various [?]awk commands are not builtins so you also cheated... ;oD

At sea...
It would have been interesting to see your result too...

One very serious question however:-

How accurate is either "[?]awk's" or "ksh's" floating point?
Would there be a situation where there would be a false result due to a floating point __error__?

Bazza...

Not too related, but i use this check for int or floating point in ksh93

# We need KSH93 for this, for ksh88 we will need to replace the second case with proper syntax which is a bit larger and more complex.
function checkint {
case "${1}" in
        +([0-9]))
        printf "%s \n" "Int check OK with input ${1}"
        return 0
;;
        +([0-9]).{1,3}([0-9]))
        printf "%s \n" "Floating point upto 3 decimals check OK with input ${1}"
        return 0
;;
        *)
        printf "%s \n" "Int check failed with input ${1}"
        return 1
;;
esac
}

Actually you haven't. It is perfectly OK to have discussions and exhibit different opinions. It is also perfectly OK to expose factual errors in the reasoning of others. (Who would know that better than me who has been - rightfully - corrected over and over again here, most oftenly by Don Cragun.)

I think that, even restricting yourself to integer arithmetic and built-ins, you could speed up the process by using better algorithms. For instance, an application of Newtons method of caluclating the roots of differentiable functions (also called "Netwon-Raphson-method):

Suppose some real function f: [a,b] -> R , which is differentiable everywhere on the intervall [a,b] with values only in R.

It can easily be shown that, starting from an initial guess x[n] (as long as x[n] is reasonably close to x), a better guess x[n+1] can be calculated using the formula

                   f( x  )
                       n
    x     = x  - -------------
      n+1    n     f'( x  )
                        n

by solving for the tangents equation at f(x)=y and then solving for the x-intercept of this line (Simpsons approximation).

Applying this to get the zeroes of the iteration function f(x) = x**2 - a brings us to the "babylonian method" or "Heron's method" of calculating roots:

Because of the deriative f'(x) = 2x for the solution sqrt(a) we get the approximation

                    2                 
                   x  - a            
                    n         1          a  
   x     := x  - --------- = --- ( x  + ---- )
     n+1     n    2 x         2     n    x
                     n                    n

by which we calculate a series of xi's (i=0,1,2,...), which will converge against x, until a sufficient approximation is reached.

kshs floats are double-precision, as described at KSH-93 - The KornShell Command and Programming Language:

I hope this helps.

bakunin

Hi bakunin...

i wrote a version of Newton's method using ASIC for MS-DOS years ago as the 'SQRT' command/function did not exist in its compiler. It was accurate enough after 10 iterations.

https://en.wikipedia.org/wiki/ASIC\_programming_language

But I really enjoy forced restriction and non-standard solutions to a problem.

My bash version requires no knowledge of serious maths whatsoever and uses an observation that is little known to the general public...

I wasn't really interested in speed just the unusualness, (is there such a word?), of the idea to see how easy it was in integer only bash scripting...

I suspect there might be an upper limit to this method but I don't know what the various shells upper arithmetic limits are, I am assuming 2^31 for 32s bit and 2^63 for 64 bits for positive numbers.

Not sure how one would apply the other formula(e) to bash's integer integer arithmetic without the use of either awk and or bc/dc...

Finally I was aware of the general accuracy of the floating point maths but not sure how for example '1/3' would be interpreted:-

Last login: Fri Aug 21 12:50:02 on console
AMIGA:barrywalker~> ksh
AMIGA:uw> x=$((1/3))   
AMIGA:uw> echo $x
0
AMIGA:uw> x=$((1.0/3.0))
AMIGA:uw> echo $x 
0.333333333333333333
AMIGA:uw> echo $(($x*3.0))
0.999999999999999999
AMIGA:uw> _

You see my point as echo $((int($x*3.0))) would round down to 0, zero...

You mean that sqares of consecutive natural numbers have the property x[i+1]**2 - x**2 = 2x+1 = 2x[i+1]-1 ?

OK, here is a short integer implementation, which comes "from top", not "from bottom" (meaning the guesses are always higher than the real value and dropping to it).

#! /bin/bash

pNextGuess ()
{
     typeset -i iLastGuess=$1
     typeset -i iSquare=$2
     typeset -i iNextGuess=$(( ( iLastGuess + iSquare / iLastGuess ) / 2 ))

     echo $iNextGuess

     return 0
}

# main ()
typeset -i iSquare=$1
typeset -i iGuess=1
typeset -i iNextGuess=0

while : ; do
     iNextGuess=$(pNextGuess $iGuess $iSquare)
     if [ $iNextGuess -eq $iGuess ] ; then
          if [ $(( iGuess * iGuess )) -eq $iSquare ] ; then
               echo "Solution is $iGuess"
          else
               echo "no perfect square, best approximation is $iGuess"
          fi
          exit 0
     fi
     iGuess=iNextGuess
done

A small problem, though: some values cause the guesses to oszillate between two values because of the integers: 960 (961 would be 31**2) is such a number, for instance, because the final guesses will be 30, 31, 30, 31, 30, ....

I (don't think this helps anyone, but) hope this increases the fun.

bakunin

Hi wisecracker,
In my defense, you originally stated:

I see that you said that you used bash (twice), but I don't see any requirement that alternative proposals should use bash nor that they should only use shell built-ins. The INTEGER specification was in the title, but not in the text of the message. I missed that point, so I admit that I did cheat. :o

Moving on to your later question:

I provided a similar degree of INPUT error correction with added checking to avoid rounding errors that could arise from processing floating point input values such as:

perfect_square 1.44
1.44 is the perfect square of 1.19999999999999996...

With your bash script using 64-bit signed integer arithmetic, I would expect that the largest square your script could process would be:

perfect_square 9223372030926249001
9223372030926249001 is the perfect square of 3037000499...

(i.e., the largest perfect square <= 9223372036854775807 [2**63 - 1]). With ksh93 and awk there could be some issues due to floating point artifacts with operands greater than 15 digits when performing floating point operations (only square root calculation in this script uses floating point arithmetic), but since the check at the end was using integer arithmetic, I thought the test should still be valid. But, going back and checking what was going on, I seem to have found a bug in the ksh test -eq operator on OS X (version sh (AT&T Research) 93u+ 2012-08-01 ). When testing integer numeric values that fit in a long int (64-bit signed int on OS X), the command:

[ $square -eq $number ]

should be performing an integer test for comparison. But, I found that the commands:

[ 9223372030926249001 -eq 9223372030926249000 ]
               and
[ 9223372030926249001 -eq 9223372030926249002 ]

both evaluate to true (which means it must be testing those values with a floating point comparison that has lost low end details due to the number of significant digits).

And I realized that the way I was testing could give false negatives when using int(xxx.9999xxx) would truncate to an integer when I needed to round to an integer. (I see other people have also commented on this while I was debugging my code last night and this morning.)

So, coding around my bug and the ksh bug, the following modified script should give you correct results for any value for which your original version would give correct results (and the runtime is not noticeably different from my earlier version):

#!/bin/ksh
# perfect_square <number>
number=$1
if [ "$number" -eq "$((int(number)))" ] > /dev/null 2>&1 
then
	if [ "$number" -lt 0 ]
	then
		echo "Warning! Integer is negative!!!"
		echo "Set input integer to the DEMO value of 99..."
		number=99
	fi
else
	echo "Invalid Argument! Set input integer to the DEMO value of 100..."
	number=100
fi
root=$((int(sqrt(number) + .5))) # Fix my bug...
square=$((root * root))
if [ $square = $number ] # "=" not "-eq" to avoid ksh bug
then
	echo "$number is the perfect square of $root..."
	exit 0
else
	echo "Integer $number is not a perfect square..."
	exit 1
fi

With the .5 added to the int() argument, that takes care of my bug. Note that square=$((root * root)) may yield a floating point value if and only if the result overflows a signed long int. And, if that happen, there will be a decimal point in the result. This will cause the script to report that the result is not a square even if it happens to be a square (such as with 100000000000000000000 ), but that cannot happen for any 63 bit integer value that it a perfect square that could be processed by your bash script. I should add a test at the start to check for input larger than LONG_MAX, but I'm not going to try to do that (when I can't trust the test -gt operator) until I get some sleep.

Please read all of the above carefully. It has been a while since I put in an all-nighter to track down a coding bug... It is now 7:20am and I'm going to bed. :stuck_out_tongue:

1 Like

Correct me if i'm wrong, but doesnt integer stop at like... 2.7bil?
And if i look at:

[ 9223372030926249001 -eq 9223372030926249000 ]

And make it easier (for me) to read: 9'223'372'030'926'249'001 , which are (rounded up) 10 billion billions, or about 400 times the max of a 'reliable' integer value.
And as on overflow, they stop counting at the ~2.7bils
(overnighted and not used to such high numbers, might be mathematical incorrect proportions, but you get the idea)

One would need (afaik) a long int or int64, which are both (afaik) not available to the bash shell.

hth

BASH has had 64 bit integers for ages now. Really ancient versions (pre-3.x) won't have it, though, and this only refers to BASH, not Bourne shells in general.

So much for getting some sleep...

According to the standards, conforming shells must use signed long int (or something else that provides the same results when working with values in the range of a signed long int). With the bash being talked about on this thread:

bash-3.2$ getconf LONG_MAX
9223372036854775807
bash-3.2$ test 9223372030926249001 -eq 9223372030926249000
bash-3.2$ echo $?
1
bash-3.2$ test 9223372030926249001 -eq 9223372030926249001
bash-3.2$ echo $?
0
bash-3.2$ test 9223372030926249001 -eq 92233720309262490009
bash: test: 92233720309262490009: integer expression expected
bash-3.2$ 

shows the results required for a conforming shell in the 1st two examples and shows an allowed (and expected) result for the 3rd case where the last operand is out of the range of a long int (signed or unsigned).

I agree that it is hard to count the digits when looking at numbers that big, but the shell doesn't allow thousands separators in integer constants given to it in expressions nor when they are used as operands for shell built-ins expecting integer arguments.

1 Like

Binary search was a good idea. Needed to watch a few edge cases.

#!/bin/bash

VAL=${1-100}
MN=1
MX="$((VAL))"
[ "$MX" -ge 3037000499 ] && MX=3037000499 # Square root of highest possible perfect square

function die() {
        local CODE=$1 ; shift
        echo "$@" >&2 ; exit $CODE
}

[ "$VAL" -lt 0 ] && die 1 "Negative numbers not allowed"

while (((MX-MN) > 1 ))
do
        if (( (((MN+MX)/2) * ((MN+MX)/2)) > VAL )) ; then
                MX=$(( (MN+MX) / 2 ))
        elif (( (((MN+MX)/2) * ((MN+MX)/2)) < VAL )) ; then
                MN=$(( (MN+MX) / 2 ))
        else
                break;
        fi
done

(( (((MN+MX)/2) * ((MN+MX)/2)) == VAL )) &&
        die 0 "Square root of $VAL is $(( (MN+MX)/2 ))"

die 1 "$VAL is not a perfect square"
$ time ./psquare.sh $(( 300000 * 300000 ))
MN=299968, MX=300032
Square root of 90000000000 is 300000

real    0m0.004s
user    0m0.003s
sys     0m0.000s
$

That is another stroke against bash being standards compliant, then. I recall getting 64-bit numbers on systems where 'long int' is 32 bit (though honestly was quite happy to have them).

If a shell uses signed long long int (64-bits) in an environment where a signed long int is 32-bits, that is still conforming. Any operation using 64-bit ints instead of 32-bit ints will get exactly the same results for any arithmetic operation that does not cause overflow when using 32-bit signed values.

1 Like

Hi all...

For those that don't know, ths is the method used...

Take all of the odd numbers in a series:-

1, 3, 5, 7, 9, 11, 13........

Then:-
[1] 1 = 1 = 1^2

[2] 1+3 = 4 = 2^2

[3] 4+5 = 1+3+5 = 9 = 3^2

[4] 9+7 = 1+3+5+7 = 16 = 4^2

[5] 16+9 = 1+3+5+7+9 = 25 = 5^2

[6] 25+11 = 1+3+5+7+9+11 = 36 = 6^2

[7] 36+13 = 1+3+5+7+9+11+13 = 49 = 7^2

And so on...

It is as simple as that, nothing more nothing less...
The linear addition by one of the square bracketed __line_numbers__ are the integer, (perfect), square root...

Hope this makes sense...

As I quoted it is a fun tongue-in-cheek method...

OK. I think I'm caught up on my sleep. And, I think I have a new script that meets all of the requirements ( bash script using only integer arithmetic and only using shell built-ins running on Mac OS X on a MacBook Pro (assembled in mid 2014)). I am, however, using OS X version 10.10.5 instead of version 10.7.5 that wisecracker was using.

The bash script wisecracker provided in post #1 in this thread mostly works fine for relatively small values, but takes a LONG time for relatively large values. I was surprised that it said that 0 is not a perfect square instead of saying that the minimum value it would process is 1. I wanted to see what it would do when invoked as:

perfect_square 1000000002000000001

I wasn't surprised that it came up with the correct answer:

1000000002000000001 is the perfect square of 1000000001...

but a ps run shortly before it completed showed that it used more than 595 cpu minutes to compute that result.

While that was consuming one core of one cpu, I played around with Corona688's bash script in post #16 in this thread. It correctly reported that 0 is a perfect square (which surprised me since it always uses 1 as the low end of its binary search range). And, due to the way it calculates the stopping point for the binary search and the way it determines the value it is processing, it says:

9223372030926249001 is not a perfect square

even though the correct response would be:

Square root of 9223372030926249001 is 3037000499

When given invalid numbers and when given valid numbers that do not fit in a signed long integer, bash prints a diagnostic message when processing that value with Corona688's script and sometimes continues processing and sometimes terminates processing at that point.

I made a slight modification to Corona688's script to read values from a file instead of just processing one command line argument.

And, I produced the following bash script which performs a more traditional binary search. It uses a little bit of number theory to reduce the range of the binary search. (An x-1 or x digit integer, where x is even, will have a square root that is x/2 digits if it has an integer square root.) It performs lots of tests to verify that the input is a valid number and is in range for a signed long int.

It reads its input from a file named file and looks for two values on each line. The 1st value is the number to be processed. The 2nd value (if present) indicates the expected results for the 1st value (-1 if it is not a perfect square or the square root if it is a perfect square). If the expected value does not match the computed value, it prints a diagnostic. If the 2nd value is not present, the result is printed, but not verified.

#!/bin/bash
# Usage: perfect_square
# Numbers to be processed are read from an input file named file.

# There is no prompting for input.

# The input file format is:
#	number_to_evaluate [expected_result]
# where number_to_evaluate is assumed to be a string of decimal digits with
# an optional leading minus sign, but no leading zeros.  This string will be
# checked to verify that is a positive decimal value <= LONG_MAX and, if it
# is, determine wheter or not it has an integer square root.  If expected
# result is given and is a positive number, it is the square root that is
# expected to be determined from the given number_to_evaluate.  If it is -1,
# number_to_evaluate is not a perfect square.  If expected_result is not
# given, the code will not verify the results computed against the
# expected_value.

# Note that the extensive checks used to verify valid numbers are used
# because the normal -lt, -le, eq, -ne, -ge, and -gt operators are not
# reliable with numbers that overflow a signed long integer.

# The following constants assume a 64-bit signed long int.  If your system
# has a differrent number of bits in a signed long int, these values can be
# determined for your system, respectively, with the command:
#	printf '0k%dpdvp1-d*pq\n' $(getconf LONG_MAX) | dc

# Maximum signed long int
LONG_MAX=9223372036854775807
# Maximum value that can be squared and produce a valid signed long int
MAX_SQUARE_ROOT=3037000499
# (MAX_SQUARE_ROOT - 1) ** 2
MAX_RANGE=9223372024852248004

while read num root
do	# Verify non-empty string, no leading zeros, all digits (other than
	# one allowed leading minus sign)...
	if [[ "$num" = "" ]] || [[ "$num" != "${num##*[![:digit:]-]}" ]] || \
	    [[ "$num" != "${num#[0-]0}" ]] || [[ "$num" != "${num#[0-9-]*-}" ]]
	then	printf '%s %s\n%s: "%s"\n' \
		    'Invalid number (no leading zeros, no non-digits (other' \
		    'than an optional' 'leading -), no empty strings' "$num"
		continue
	fi
	# Verify that 0 <= num <= LONG_MAX...
	if [[ ${#num} -gt ${#LONG_MAX} ]] || \
	    { [[ ${#num} -eq ${#LONG_MAX} ]] && [[ "$num" > "$LONG_MAX" ]];} || \
	    [[ num -lt 1 ]]
	then	printf "Out of range: 0 < x <= $LONG_MAX: x=$num\n"
		continue
	fi
	# Set binary search range...
	if [[ num -gt MAX_RANGE ]]
	then	low=$MAX_SQUARE_ROOT
		high=$MAX_SQUARE_ROOT
	else	# Note that a number containing x or x+1 digits will have a
		# square root that contains x / 2 digits...
		low=$((10 ** ((${#num} - 1) / 2)))
		if [[ ${#num} -eq ${#LONG_MAX} ]]
		then	high=$((MAX_SQUARE_ROOT - 1))
			[[ num -lt high ]] && high=$num
		else	high=$((10 ** ((${#num} - 1) / 2 + 1) - 1))
		fi
	fi
#	printf 'num=%d(%d), root=%s(%d), low=%d(%d), high=%d(%d)\n' \
#	    $num ${#num} "$root" ${#root} $low ${#low} $high ${#high}
	# Search the established range...
	while [[ low -le high ]]
	do	mid=$(((low + high) / 2))
		square=$((mid * mid))
#		printf '\tlow=%d, mid=%d, high=%d, square=%d\n' $low $mid \
#		    $high $square
		if [[ square -eq num ]]
		then	echo "Square root of $num is $mid"
			[[ "$root" != "" ]] && [[ mid -ne root ]] && \
			    echo "*** Expected \"$root\""
			continue 2
		fi
		[[ square -lt num ]] && low=$((mid + 1)) || high=$((mid - 1))
	done
	echo "$num is not a perfect square"
	[[ "$root" != "" ]] && [[ root -ne -1 ]] && echo "*** Expected \"$root\""
done < file

With the following input in file :

-10000000000000000000000000000 -1
-1 -1
0 0
1 1
2 -1
3 -1
4 2
5 -1
8 -1
9 3
10 -1
1517823 -1
1517824 1232
1517825 -1
1520288 -1
1520289 1233
1520290 -1
1522755 -1
1522756 1234
1522757 -1
1524153208355 -1
1524153208356 1234566
1524153208357 -1
1524155677488 -1
1524155677489 1234567
1524155677490 -1
1524158146623 -1
1524158146624 1234568
1524158146625 -1
9754610360615760 -1
9754610360615761 98765431
9754610360615762 -1
9754610558146623 -1
9754610558146624 98765432
9754610558146625 -1
9754610755677488 -1
9754610755677489 98765433
9754610755677490 -1
1000000002000000000 -1
1000000002000000001 1000000001
1000000002000000002 -1
1000000004000000003 -1
1000000004000000004 1000000002
1000000004000000005 -1
1000000006000000008 -1
1000000006000000009 1000000003
1000000006000000010 -1
9223372024852248003 -1
9223372024852248004 3037000498
9223372024852248005 -1
9223372030926249000 -1
9223372030926249001 3037000499
9223372030926249002 -1
9223372036854775806 -1
9223372036854775807 -1
9223372036854775808 -1
9223372037000249999 -1
9223372037000250000 3037000500
9223372037000250001 -1
9999999999999999999 -1
10000000000000000000000000000 100000000000000
All of the following shold be rejected as invalid numbers (not out of range)...
abcd
001
-01
1-2
-abc
-0

123a
abc-
0-

it produces the output:

Out of range: 0 < x <= 9223372036854775807: x=-10000000000000000000000000000
Out of range: 0 < x <= 9223372036854775807: x=-1
Out of range: 0 < x <= 9223372036854775807: x=0
Square root of 1 is 1
2 is not a perfect square
3 is not a perfect square
Square root of 4 is 2
5 is not a perfect square
8 is not a perfect square
Square root of 9 is 3
10 is not a perfect square
1517823 is not a perfect square
Square root of 1517824 is 1232
1517825 is not a perfect square
1520288 is not a perfect square
Square root of 1520289 is 1233
1520290 is not a perfect square
1522755 is not a perfect square
Square root of 1522756 is 1234
1522757 is not a perfect square
1524153208355 is not a perfect square
Square root of 1524153208356 is 1234566
1524153208357 is not a perfect square
1524155677488 is not a perfect square
Square root of 1524155677489 is 1234567
1524155677490 is not a perfect square
1524158146623 is not a perfect square
Square root of 1524158146624 is 1234568
1524158146625 is not a perfect square
9754610360615760 is not a perfect square
Square root of 9754610360615761 is 98765431
9754610360615762 is not a perfect square
9754610558146623 is not a perfect square
Square root of 9754610558146624 is 98765432
9754610558146625 is not a perfect square
9754610755677488 is not a perfect square
Square root of 9754610755677489 is 98765433
9754610755677490 is not a perfect square
1000000002000000000 is not a perfect square
Square root of 1000000002000000001 is 1000000001
1000000002000000002 is not a perfect square
1000000004000000003 is not a perfect square
Square root of 1000000004000000004 is 1000000002
1000000004000000005 is not a perfect square
1000000006000000008 is not a perfect square
Square root of 1000000006000000009 is 1000000003
1000000006000000010 is not a perfect square
9223372024852248003 is not a perfect square
Square root of 9223372024852248004 is 3037000498
9223372024852248005 is not a perfect square
9223372030926249000 is not a perfect square
Square root of 9223372030926249001 is 3037000499
9223372030926249002 is not a perfect square
9223372036854775806 is not a perfect square
9223372036854775807 is not a perfect square
Out of range: 0 < x <= 9223372036854775807: x=9223372036854775808
Out of range: 0 < x <= 9223372036854775807: x=9223372037000249999
Out of range: 0 < x <= 9223372036854775807: x=9223372037000250000
Out of range: 0 < x <= 9223372036854775807: x=9223372037000250001
Out of range: 0 < x <= 9223372036854775807: x=9999999999999999999
Out of range: 0 < x <= 9223372036854775807: x=10000000000000000000000000000
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "All"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "abcd"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "001"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "-01"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "1-2"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "-abc"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "-0"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: ""
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "123a"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "abc-"
Invalid number (no leading zeros, no non-digits (other than an optional
leading -), no empty strings: "0-"

with average times:

real	0m0.042s
user	0m0.037s
sys	0m0.003s

for 10 runs. With the same input file, the modified version of Corona688's script produces the output:

./Corona: line 14: [: -10000000000000000000000000000: integer expression expected
-10000000000000000000000000000 is not a perfect square
Negative numbers not allowed
Square root of 0 is 0
Square root of 1 is 1
2 is not a perfect square
3 is not a perfect square
Square root of 4 is 2
5 is not a perfect square
8 is not a perfect square
Square root of 9 is 3
10 is not a perfect square
1517823 is not a perfect square
Square root of 1517824 is 1232
1517825 is not a perfect square
1520288 is not a perfect square
Square root of 1520289 is 1233
1520290 is not a perfect square
1522755 is not a perfect square
Square root of 1522756 is 1234
1522757 is not a perfect square
1524153208355 is not a perfect square
Square root of 1524153208356 is 1234566
1524153208357 is not a perfect square
1524155677488 is not a perfect square
Square root of 1524155677489 is 1234567
1524155677490 is not a perfect square
1524158146623 is not a perfect square
Square root of 1524158146624 is 1234568
1524158146625 is not a perfect square
9754610360615760 is not a perfect square
Square root of 9754610360615761 is 98765431
9754610360615762 is not a perfect square
9754610558146623 is not a perfect square
Square root of 9754610558146624 is 98765432
9754610558146625 is not a perfect square
9754610755677488 is not a perfect square
Square root of 9754610755677489 is 98765433
9754610755677490 is not a perfect square
1000000002000000000 is not a perfect square
Square root of 1000000002000000001 is 1000000001
1000000002000000002 is not a perfect square
1000000004000000003 is not a perfect square
Square root of 1000000004000000004 is 1000000002
1000000004000000005 is not a perfect square
1000000006000000008 is not a perfect square
Square root of 1000000006000000009 is 1000000003
1000000006000000010 is not a perfect square
9223372024852248003 is not a perfect square
Square root of 9223372024852248004 is 3037000498
9223372024852248005 is not a perfect square
9223372030926249000 is not a perfect square
9223372030926249001 is not a perfect square
9223372030926249002 is not a perfect square
9223372036854775806 is not a perfect square
9223372036854775807 is not a perfect square
./Corona: line 14: [: 9223372036854775808: integer expression expected
9223372036854775808 is not a perfect square
./Corona: line 14: [: 9223372037000249999: integer expression expected
9223372037000249999 is not a perfect square
./Corona: line 14: [: 9223372037000250000: integer expression expected
9223372037000250000 is not a perfect square
./Corona: line 14: [: 9223372037000250001: integer expression expected
9223372037000250001 is not a perfect square
./Corona: line 14: [: 9999999999999999999: integer expression expected
9999999999999999999 is not a perfect square
./Corona: line 14: [: 10000000000000000000000000000: integer expression expected
10000000000000000000000000000 is not a perfect square
./Corona: line 14: [: abcd: integer expression expected
Square root of abcd is 0
Square root of 001 is 1
Negative numbers not allowed
./Corona: line 14: [: 1-2: integer expression expected
1-2 is not a perfect square
./Corona: line 14: [: -abc: integer expression expected
Square root of -abc is 0
Square root of -0 is 0
./Corona: line 14: [: : integer expression expected
Square root of  is 0
./Corona: line 11: 123a: value too great for base (error token is "123a"

with average times:

real	0m0.041s
user	0m0.036s
sys	0m0.003s

for 10 runs. Note that it is expected to run faster than the above script because it doesn't perform any error checking and it doesn't process the last two input lines because it dies on any of the last three input lines. I was surprised that the times were so close. (Apparently reducing the binary search range made up for the extra data verification tests.)

Note also that I am not saying that Corona688's script should perform any error checking; that was not a requirement for this project. I machine-generated most of the input file (after I found the anomalies in the results from the ksh script I presented earlier) and added code to verify that the results computed matched known results as a debugging aid.

3 Likes