Find neighbouring prime numbers using BASH - bash

I had a pretty hard homework (hard for beginners like me).
We had to find given number's nearest neighbouring primes.
User input must be a two-digit number and can be prime. The results must be printed on screen, seperated by whitespaces.
For input validaton:
while read -p "Give me a two-digit numer: " n && [[ -z $n ]] || [[ ! $n =~ ^[0-9]+$ ]] || [ $n -lt 10 ] || [ $n -gt 99 ]
do
echo -e "\e[31mLet's try that again ...\e[0m"
done
I was thinking about using an already initialized array, containing the prime numbers from 7 to 101 and somehow compare it with the user input.
# Initialize an array with possible prime numbers
primes=(7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101)
# Create an inverted version of 'primes' array ... because of reasons ...
for (( i="${#primes[#]}-1" ; i>=0 ; i-- ));
do
rprimes+="${primes[i]} "
done
Who else likes 'for' loops?
# To find the next prime number
i=0
for i in ${primes[#]}
do
if [ $i -gt $n ]
then
gtn=$i
break
fi
done
# To find the previous prime number in the inverted 'rprimes' array
for i in ${rprimes[#]}
do
if [ $i -lt $n ]
then
ltn=$i
break
fi
done
# Results:
echo "$ltn $n $gtn"
The script works, but I would really like to know, if there is a way to find the 'previous' value without the need to create an inverted array.
Sorry for mah english!
Solution/Edit:
for (( i=0; for (( i=0; i<=${#primes[#]}; i++ ));
do
if [ ${primes[i]} -lt $n ] && [ ${primes[i+1]} -ge $n ]
then
ltn=${primes[i]}
break
fi
done

You already understand for loops clearly, since you use one to create the reversed array rprimes. Just run a forward for on the primes array. Isn't the condition for ltn=${primes[i]} just
[ ${primes[i]} -lt $n ] && [ ${primes[i+1]} -ge $n ]
?
And the condition for gtn=${primes[i+1]} likewise
[ ${primes[i]} -le $n ] && [ ${primes[i+1]} -gt $n ]
?

Just find the index of the prime you're interested in, it's then easy to look around just by ±1:
for (( i=0; i<${#primes[#]}; i++ )) ; do
if (( n <= primes[i] )) ; then
p=$(( n == primes[i] ))
echo ${primes[i-1]} $n ${primes[i+p]}
break
fi
done

Related

Create variable within a range of numbers except a list of numbers

I have the following variable with a list of numbers
vlist="1 13 20 21 22 24 25 28 240 131 133 136 138 224"
In the next loop I want to input a number between 1 - 250 except the numbers in vlist
while :; do
echo -en 'Number : ' ; read -n3 cvip ; echo
[[ $cvip =~ ^[0-9]+$ ]] || { echo -e '\nSorry input a number between 1 and 239 except '$vlist'\n' ; continue; }
cvip=$(expr ${cvip} + 0)
if ((cvip >= 1 && cvip <= 250)); then break ; else echo -e '\nNumber out of Range, input a number between 1 and 239 except '$vlist'\n' ; fi
done
Ηow can I enter the list exception inside the if statement range
If using bash, one approach is to store the bad numbers as keys in an associative array and see if that particular key exists when validating a number:
#!/usr/bin/env bash
vlist="1 13 20 21 22 24 25 28 240 131 133 136 138 224"
declare -A nums
for num in $vlist; do
nums[$num]=1
done
while true; do
while read -p "Number: " -r num; do
if [[ ! $num =~ ^[[:digit:]]+$ ]]; then
echo "Input an integer."
elif [[ ${nums[$num]:-0} -eq 1 ]]; then
echo "Input a number between 1 and 250 except '$vlist'"
elif [[ $num -lt 1 || $num -gt 250 ]]; then
echo "Input an integer between 1 and 250"
else
break
fi
done
printf "You successfully inputted %d\n" "$num"
done
The important bit is ${nums[$num]:-0}, which expands to the value of that element of the associative array if that key exists and is not null, or 0 otherwise. As noted by Glenn in a comment, in bash 4.3 or newer, [[ -v nums[$num] ]] works too for testing to see if a given key exists, so [[ ${nums[$num]:-0} -eq 1 ]] in the above could be replaced with it.

Bash nested conditionals show unexpected behavior

please take a look at this:
This should echo X10, but echoes jackpot... can anyone see why it doesn't behave as it should?
Probably just some mistake I made that do not throw errors?
dice1=1
dice2=40
#These two lines are just tests to see if my brain still function:
echo "Is dice 1 less than 2? $(($dice1 < 2))"
echo "Is dice 2 between 6 and 54? $(($dice2 > 5 && $dice2 < 55))"
if [[ $dice1 == 1 ]]
then
if [[ $dice2 < 6 ]]
then
#dice1 has to be equal 1 and dice2 less than 6:
echo "jackpot"
else
#Since dice2 is larger than 5, if smaller than 55
#it should be between 6 and 54...
if [[ $dice2 < 55 ]]
then
echo "X10"
else
echo "X5"
fi
fi
else
echo "Dice one is not equal 1."
fi
When used with [[, the < and > operators sort lexicographically using the current locale.
I see two options.
Do the comparison in arithmetic context:
if (( $dice1 == 1 ))
then
if (( $dice2 < 6 ))
or the old-fashioned way:
if [[ $dice1 -eq 1 ]]
then
if [[ $dice2 -lt 6 ]]

bash - for loop different iterations

I know how to do sequential for loops like:
for i in $(seq 0 63)
do
echo $i
done
This would print 0-63 .
But what if I wanted certain numbers looped like only 0, 5, 25, 43, 44, 51, 54.
I know I could do the following:
for i in $(seq 0 63)
do
if [ "$i" -eq 0 ] || [ "$i" -eq 5 ] || [ "$i" -eq 25 ] || [ "$i" -eq 43 ] || [ "$i" -eq 44 ] || [ "$i" -eq 51 ] || [ "$i" -eq 54 ]; then
echo $i
fi
done
But the result will be that it still goes through those loop iterations, and is not an efficient solution.
Is there something I can use with seq to describe these certain numbers 0,5,25,43,44,51,54?
If you want to iterate over a known list, you can simply store the elements, space-separated, in a variable and loop over that:
nums="0 5 25 43 44 51 54";
for i in $nums;
do echo $i;
done;

Using if and controlling numbers in ksh

I would like to download file with the format cars000.txt, cars003.txt,cars006.txt, till cars105.txt...interval of 3 as you can see
I use the following code in ksh, but after downloading cars012.txt, it fails, it begins to download cars13.txt,...and I don't wish it. What does it fails in the code?
FHR=000
while [ $FHR -le 105 ]
do
file=cars${FHR}.txt
wget http://${dir_target}/${file}
(( FHR = $FHR + 03 ))
echo $FHR
if [[ $FHR -le 10 ]]; then FHR="00"$FHR
else FHR="0"$FHR
fi
done
You should decide: is FHR a string, a decimal or an octal.
You are mixing them currently.
Try the next improvement:
FHR=0
while [ ${FHR} -le 105 ]; do
file=cars${FHR}.txt
(( FHR = FHR + 3 ))
echo Without leading zero: FHR=${FHR}
if [[ $FHR -le 10 ]]; then
echo "FHR=00${FHR}"
else
echo "FHR=0${FHR}"
fi
sleep 1
done
(The next improvement might be using printf or awk and no zero for 102/105)

shell program does not show the value of the character given as input?

I am inputting a single character from the user and trying to print the ascii value of the character if it's value is >=97 and <=121
This is my code and it does not work.
echo "Enter a character"
read n
if ["'${n}" -ge 97 and "'${n}" -le 121]
then
print "%d","'$n"
fi
Error:
ascii.sh: 3: ascii.sh: ['a: not found
[ is a command in shell, aka test command.
You need spaces around [ and ].
Moreover, in order to compare integers you need to use < and >.
EDIT: In order to fix, you could say:
read n
asc=$(printf "%d" "'$n")
[[ "$asc" > 97 ]] && [[ "$asc" < 122 ]] && echo $asc
If you're using sh, you could change the last line to:
[ "$asc" -gt 97 ] && [ "$asc" -le 121 ] && echo $asc
Something like this will make it:
#!/bin/sh
min=97
max=121
echo "Enter a character"
read n
value=$(printf "%d" "'$n")
printf "The ASCII character of %s is %d.\n" "$n" "$value"
if [ "${value}" -le $min ] && [ "${value}" -le $max ]
then
printf "%d not in the range.\n" "$value"
else
printf "%d in the range.\n" "$value"
fi
Things that I changed:
- printf instead of print.
- ["'${n}" -ge 97 and "'${n}" -le 121] condition needed to be splited in two blocks:
if [ "${n}" -le 97 ] && [ "${n}" -le 121 ]

Resources