Fastest algorithm to print all 16-bit binary numbers in Powershell? - algorithm

The PowerShell should print all 16-bit binary number with leading zeroes like this:
0000000000000000
0000000000000001
0000000000000010
0000000000000011
...
1111111111111100
1111111111111101
1111111111111110
1111111111111111
My current code looks like this:
0 .. 65535 | % { "{0:D16}" -f [int64][convert]::ToString($_,2) }
But I am wondering if there are other algorithms that would perform the task faster.

tl;dr The following approach using caching and an array look-up is faster, but such may not be realized due to other bottlenecks.
Here is a version which uses caching. However, as per Measure-Command there is no significant improvement (3.5 vs 3.8 elapsed seconds) - I was expecting to see a much larger difference.
$l = #(0) * 256
0..255 | % {
$l[$_] = "{0:D8}" -f [int64][convert]::ToString($_,2)
}
0 .. 65535 | % {
$l[$_ / 256 ] + $l[$_ -band 255] # no -shr in PS before 3.0
}
There are two "slow" parts with this problem. One is the use of %{} is slow compared to a plain loop. Using the above modified to the following (which isn't very useful) completes in 0.3 seconds.
For ($i = 0; $i -lt 65535; $i = $i + 1) {
$line = $l[$i / 256 ] + $l[$i -band 255]
}
While a similarly modified, and equally useless, version of the original completes in 0.5 seconds. This is a good bit slower than the cached proposed version, even though the proposed approach does not ultimately affect the bottleneck or wall-clock.
For ($i = 0; $i -lt 65535; $i = $i + 1) {
$line = "{0:D16}" -f [int64][convert]::ToString($i,2)
}
Usefully collecting output manually, with a presized array, is also much faster than %{} and it runs in 0.5 seconds for my version - it will run a bit slower for the original approach, say 0.8 seconds.
$r = #("") * 65536
# ..
For ($i = 0; $i -lt 65535; $i = $i + 1) {
$r[$i] = $l[$i / 256 ] + $l[$i -band 255]
}
Secondly, actually materializing with Write-Output is very slow and is slower than collecting the result with %{}. Using either Write-Output or Write | Output-File .. results in times exceeding 8 seconds.
$r = #("") * 65536
$l = #(0) * 256
For ($i = 0; $i -lt 256; $i = $i + 1) {
$l[$i] = "{0:D8}" -f [int64][convert]::ToString($i, 2)
}
For ($i = 0; $i -lt 65535; $i = $i + 1) {
$r[$i] = $l[$i / 256 ] + $l[$i -band 255]
}
# to here in about 0.5 seconds
Write $r | Out-File results.txt
# almost another 8 seconds to get here

Related

How do I increment a number in middle of the string

I am trying to increment a number in the middle of the string. Tried many ways but didn't find a solution. Any ideas in shell script
Ex:- i have a string sam_2.0_protected_dev_branch. I want to increment the number at the middle of the string. So the output should look like
sam_2.0_kumar_dev_branch
sam_2.1_kumar_dev_branch
sam_2.2_kumar_dev_branch
...
Maybe instead of trying to change the string, divide it in 3 parts. text, number and text. you can increment the number and then make one string with that 3 parts.
Sorry if the terminology isn't right
here is a proper documentation: https://ss64.com/ps/syntax-concat.html
Code :
$version = 1.0
$nameOne = "sam_"
$nametwo = "_protected_dev_branch"
for ($i = 1; $i -lt 5; $i++)
{
$fullName = $nameOne + $version + $nametwo
Write-Host $fullName
$version = $version + 0.1
}
Output :
sam_1_protected_dev_branch
sam_1.1_protected_dev_branch
sam_1.2_protected_dev_branch
sam_1.3_protected_dev_branch
there's a way to to force a number to show number of numbers after the comma, but I will let you deal with it
$version = 1
for ($i = 1; $i -lt 5; $i++)
{
$fullName = "sam_2." + $version + "_protected_dev_branch"
Write-Host $fullName
$version = $version + 1
}

Is there a way to analyze packet intervals in order to output # of packets per second?

I have about 54,000 packets to analyze and I am trying to determine the average # of packets per second (as well as the min and max # of packets during a given second)
My input file is a single column of the packet times (see sample below):
0.004
0.015
0.030
0.050
..
..
1999.99
I've used awk to determine the timing deltas but can't figure out a way to parse out the chunks of time to get an output of:
0-1s = 10 packets
1-2s = 15 packets
etc
Here is an example of how you can use awk to get the desired output.
Suppose your original input file is sample.txt, first thing to do is reverse sort it (sort -nr) then you can supply awk with the newly sorted file along with the time variable through awk "-v" argument. Perform your tests inside awk, make use of "next" to skip lines and "exit" to quit the awk script when needed.
#!/bin/bash
#
for i in 0 1 2 3
do
sort -nr sample.txt |awk -v time=$i 'BEGIN{number=0}''{
if($1>=(time+1)){next}
else if( $1>=time && $1 <(time+1))
{number+=1}
else{
printf "[ %d - %d [ : %d records\n",time,time+1,number;exit}
}'
done
Here's the sample file:
0.1
0.2
0.8
.
.
0.94
.
.
1.5
1.9
.
3.0
3.6
Here's the program's output:
[ 1 - 2 [ : 5 records
[ 2 - 3 [ : 8 records
[ 3 - 4 [ : 2 records
Hope this helps !
Would you please try the followings:
With bash:
max=0
while read -r line; do
i=${line%.*} # extract the integer part
a[$i]=$(( ${a[$i]} + 1 )) # increment the array element
(( i > max )) && max=$i # update the maximum index
done < sample.txt
# report the summary
for (( i=0; i<=max; i++ )); do
printf "%d-%ds = %d packets\n" "$i" $(( i+1 )) "${a[$i]}"
done
With AWK:
awk '
{
i = int($0)
a[i]++
if (i > max) max = i
}
END {
for (i=0; i<=max; i++)
printf("%d-%ds = %d packets\n", i, i+1, a[i])
}' sample.txt
sample.txt:
0.185
0.274
0.802
1.204
1.375
1.636
1.700
1.774
1.963
2.044
2.112
2.236
2.273
2.642
2.882
3.000
3.141
5.023
5.082
Output:
0-1s = 3 packets
1-2s = 6 packets
2-3s = 6 packets
3-4s = 2 packets
4-5s = 0 packets
5-6s = 2 packets
Hope this helps.

Java String.hashCode() implementation in Bash

I am trying to imlplement String.hashCode() function in Bash. I Couldn't figure out the bug.
this is my sample implementation
function hashCode(){ #similar function to java String.hashCode()
foo=$1
echo $foo
h=0
for (( i=0; i<${#foo}; i++ )); do
val=$(ord ${foo:$i:1})
echo $val
if ((31 * h + val > 2147483647))
then
h=$((-2147483648 + (31 * h + val) % 2147483648 ))
elif ((31 * h + val < -2147483648))
then
h=$(( 2147483648 - ( 31 * h + val) % 2147483648 ))
else
h=$(( 31 * h + val))
fi
done
printf %d $h
}
function ord() { #asci to int conversion
LC_CTYPE=C printf %d "'$1"
}
Java function looks like this
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
Expected output for string "__INDEX_STAGING_DATA__0_1230ee6d-c37a-46cf-821c-55412f543fa6" is "1668783629" but the output is -148458597
Note - Have to handle java int overflow and underflow.
Vinujan, your code is working for the purpose of hashing a given string using the algorithm you have included. You do not need the ord function as you can cause the literal conversion to ASCII value with printf -v val "%d" "'${foo:$i:1}" (unless you need the LC_CTYPE=C for character set differences).
For example, with just minor tweaks to your code, it will hash the string "hello" properly:
#!/bin/bash
function hashCode() {
local foo="$1"
local -i h=0
for ((i = 0; i < ${#foo}; i++)); do
printf -v val "%d" "'${foo:$i:1}" # val is ASCII val
if ((31 * h + val > 2147483647)) # hash scheme
then
h=$((-2147483648 + (31 * h + val) % 2147483648 ))
elif ((31 * h + val < -2147483648))
then
h=$(( 2147483648 - ( 31 * h + val) % 2147483648 ))
else
h=$(( 31 * h + val))
fi
done
printf "%d" $h # final hashCode in decimal
}
hash=$(hashCode "$1")
printf "\nhashCode: 0x%02x (%d decimal)\n" $hash $hash
Example Use/Output
$ bash hashcode.sh hello
hashCode: 0x5e918d2 (99162322 decimal)
Where you look like you have problems is in the algorithm for hashing itself. For example a longer string like password will result in your scheme returning a negative 64-bit value that looks suspect, e.g.:
$ bash hashcode.sh password
hashCode: 0xffffffffb776462d (-1216985555 decimal)
This may be your intended hash, I have nothing to compare the algorithm against. Look things over, and if you still have problems, edit your question and describe exactly what problems/error/etc. you are getting when you run the script and add that output to your question.
Edit of Hash Function for Better Behavior
Without an algorithm to implement, the only thing I can do is to reformulate the algorithm you provided to be better behaved when the calculations exceed INT_MAX/INT_MIN. Looking at your existing algorithm, it appeared to make the problems worse as large numbers were encountered rather than smoothing the values to insure they remained within the bounds.
Frankly, it looked like you had omitted subtracting INT_MIN or adding INT_MAX to h before reducing the value modulo 2147483648 when it exceeded/fell below those limits. (e.g. you forgot the parenthesis around the subtraction and addition) Simply adding that to the hash algorithm seemed to produce better behavior and your desired output.
I also save the result of your hash calculation in hval, so that it is not computed multiple times each loop, e.g.
function hashCode() {
local foo="$1"
local -i h=0
for ((i = 0; i < ${#foo}; i++)); do
printf -v val "%d" "'${foo:$i:1}" # val is ASCII val
hval=$((31 * h + val))
if ((hval > 2147483647)) # hash scheme
then
h=$(( (hval - 2147483648) % 2147483648 ))
elif ((hval < -2147483648))
then
h=$(( (hval + 2147483648) % 2147483648 ))
else
h=$(( hval ))
fi
done
printf "%d" $h # final hashCode in decimal
}
New Values
Note the hash for "hello" remains the same (as you would expect), but the value for "password" is now better behaved and returns what looks like would be expected, instead of some sign-extended 64-bit value. E.g.,
$ bash hashcode2.sh hello
hashCode: 0x5e918d2 (99162322 decimal)
$ bash hashcode2.sh password
hashCode: 0x4889ba9b (1216985755 decimal)
And note, it does produce your expected output:
$ bash hashcode2.sh "__INDEX_STAGING_DATA__0_1230ee6d-c37a-46cf-821c-55412f543fa6"
hashCode: 0x63779e0d (1668783629 decimal)
Let me know if that is more what you were attempting to do.
I got an lean solution:
hashCode() {
o=$1
h=0
for j in $(seq 1 ${#o})
do
a=$((j-1))
c=${o:$a:1}
v=$(echo -n "$c" | od -d)
i=${v:10:3}
h=$((31 * $h + $i ))
# echo -n a $a c $c i $i h $h
h=$(( (2**31-1) & $h ))
# echo -e "\t"$h
done
echo $h
}
which was wrong. :) The error was in my clever bitwise-ORing of (2**31-1) ^ $h a bitwise ANDing seems a bit wiser: (2**31-1) & $h
This might be condensed to:
hashCode() {
o=$1
h=0
for j in $(seq 1 ${#o})
do
v=$(echo -n "${$o:$((j-1)):1}" | od -d)
h=$(( (31 * $h + ${v:10:3}) & (2**31-1) ))
done
echo $h
}

BASH printf numbers with 1 char suffix

I'm trying to format a number in BASH. I'd like to replicate the byte/packet number output from iptables.
here are some examples:
258
591K
55273
37G
22244
2212
6127K
12M
114K
As you can see:
there is no thousands separator,
the field is a max of 5 characters wide,
each suffix is either: none, K, M, G, etc...
I've searched the documentation on printf but have been unable to find anything that can format a number this way. Does anyone know how to do this?
Thanks.
You could build a custom formatting with awk, something like this :
awk 'BEGIN{ u[0]=""; u[1]="K"; u[2]="M"; u[3]="G"} { n = $1; i = 0; while(n > 1000) { i+=1; n= int(n/1000) } print n u[i] } '
Input sample :
258
591000
55273
37000000000
22244
2212
6127000
12000000
114000
Output :
258
591K
55K
37G
22K
2K
6M
12M
114K
has to be done programmatically, but it's not hard
#!/bin/sh
humanFormat() {
test $x -gt 1000000000 && x=`expr x / 1000000000`G
test $x -gt 1000000 && x=`expr x / 1000000`M
test $x -gt 1000 && x=`expr x / 1000`K
}
(edited to fix execution order)

More simple math help in bash!

In the same thread as this question, I am giving this another shot and ask SO to help address how I should take care of this problem. I'm writing a bash script which needs to perform the following:
I have a circle in x and y with radius r.
I specify resolution which is the distance between points I'm checking.
I need to loop over x and y (from -r to r) and check if the current (x,y) is in the circle, but I loop over discrete i and j instead.
Then i and j need to go from -r/resolution to +r/resolution.
In the loop, what will need to happen is echo "some_text i*resolution j*resolution 15.95 cm" (note lack of $'s because I'm clueless). This output is what I'm really looking for.
My best shot so far:
r=40.5
resolution=2.5
end=$(echo "scale=0;$r/$resolution") | bc
for (( i=-end; i<=end; i++ ));do
for (( j=-end; j<=end; j++ ));do
x=$(echo "scale=5;$i*$resolution") | bc
y=$(echo "scale=5;$j*$resolution") | bc
if (( x*x + y*y <= r*r ));then <-- No, r*r will not work
echo "some_text i*resolution j*resolution 15.95 cm"
fi
done
done
I've had just about enough with bash and may look into ksh like was suggested by someone in my last question, but if anyone knows a proper way to execute this, please let me know! What ever the solution to this, it will set my future temperament towards bash scripting for sure.
You may want to include the pipe into bc in the $()'s. Instead of.
end=$(echo "scale=0;$r/$resolution") | bc
use
end=$(echo "scale=0;$r/$resolution" | bc)
should help a bit.
EDIT And here's a solution.
r=40.5
resolution=2.5
end=$(echo "scale=0;$r/$resolution" | bc)
for i in $(seq -${end} ${end}); do
for j in $(seq -${end} ${end}); do
x=$(echo "scale=5;$i*$resolution" | bc)
y=$(echo "scale=5;$j*$resolution" | bc)
check=$(echo "($x^2+$y^2)<=$r^2" | bc)
if [ ${check} -eq '1' ]; then
iRes=$(echo "$i*$resolution" | bc)
jRes=$(echo "$j*$resolution" | bc)
echo "some_text $iRes $jRes 15.95 cm"
fi
done
done
As already mentioned this problem is probably best solved using bc, awk, ksh or another scripting language.
Pure Bash. Simple problems which actually need floating point arithmetic sometimes can be transposed to some sort of fixed point arithmetic using only integers. The following solution simulates 2 decimal places after the decimal point.
There is no need for pipes and external processes inside the loops if this precision is sufficient.
factor=100 # 2 digits after the decimal point
r=4050 # the representation of 40.50
resolution=250 # the representation of 2.50
end=$(( (r/resolution)*factor )) # correct the result of the division
for (( i=-end; i<=end; i+=factor )); do
for (( j=-end; j<=end; j+=factor )); do
x=$(( (i*resolution)/factor )) # correct the result of the division
y=$(( (j*resolution)/factor )) # correct the result of the division
if [ $(( x*x + y*y )) -le $(( r*r )) ] ;then # no correction needed
echo "$x $y ... "
fi
done
done
echo -e "resolution = $((resolution/factor)).$((resolution%factor))"
echo -e "r = $((r/factor)).$((r%factor))"
you haven't heard of (g)awk ??. then you should go learn about it. It will benefit you for the long run. Translation of your bash script to awk.
awk 'BEGIN{
r=40.5
resol=2.5
end = r/resol
print end
for (i=-end;i<=end;i++) {
for( j=-end;j<=end;j++ ){
x=sprintf("%.5d",i*resol)
y=sprintf("%.5d",j*resol)
if ( x*x + y*y <= r*r ){
print ".......blah blah ......"
}
}
}
}'
It's looking more like a bc script than a Bash one any way, so here goes:
#!/usr/bin/bc -q
/* -q suppresses a welcome banner - GNU extension? */
r = 40.5
resolution = 2.5
scale = 0
end = r / resolution
scale = 5
for ( i = -end; i <= end; i++ ) {
/* moved x outside the j loop since it only changes with i */
x = i * resolution
for ( j = -end; j <= end; j++ ) {
y = j * resolution
if ( x^2 * y^2 <= r^2 ) {
/*
the next few lines output on separate lines, the quote on
a line by itself causes a newline to be created in the output
numeric output includes newlines automatically
you can comment this out and uncomment the print statement
to use it which is a GNU extension
*/
/* */
"some_text
"
i * resolution
j * resolution
"15.95 cm
"
/* */
/* non-POSIX:
print "some_text ", i * resolution, " ", j * resolution, " 15.95 cm\n"
*/
}
}
}
quit

Resources