Related
I've been learning about Python lately, but recently wanted to do a project that involved a 2D array, so I decided to switch over to C# (So sorry if my code is poor)
Basically I'm writing a program that does a crossword puzzle for me.
I have i iterating through top/bottom
I have j iterating through left to right
I find the first letter i want - lets call this "keyletter"
Now I need to look in all 8 spaces around it. If the i/j position is [1,2], I'm looking at [0,2] first.
In my code I want to change i from 1 to 0, and print, (if this is the correct letter) second letter is: [0,2]
As soon as I change i from 1 to 0 and try to print, it spits it out a million times and gets stuck.
public static void Main(string[] args)
{
string keyLetter = "g";
string keyLetter2 = "b";
string[,] crossword = new string[,]
{
{ "a", "b", "c", "d" },
{ "e", "f", "g", "h" },
{ "a", "e", "b", "c" },
{ "i", "j", "k", "l" }
};
for (int i = 0; i < crossword.GetLength(0); i++)
{
for (int j = 0; j < crossword.GetLength(1); j++)
{
if (keyLetter == crossword[i, j])
{
Console.Write(keyLetter + " is [" + i + ", " + j + "]");
Console.WriteLine();
Console.WriteLine();
Console.Write("i is: " + i);
Console.WriteLine();
Console.Write("j is: " + j);
Console.WriteLine();
if (keyLetter2 == crossword[i - 1, j])
{
// i--;
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
else if (keyLetter2 == crossword[i + 1, j])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
/*
else if (keyLetter2 == crossword[i + 1, j - 1])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
else if (keyLetter2 == crossword[i, j - 1])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
else if (keyLetter2 == crossword[i - 1, j - 1])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
else if (keyLetter2 == crossword[i + 1, j + 1])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
else if (keyLetter2 == crossword[i, j + 1])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}
else if (keyLetter2 == crossword[i - 1, j + 1])
{
Console.Write("[i/j] position for " + keyLetter2 + " is [" + i +
", " + j + "]");
}*/
Console.WriteLine();
}
}
}
}
I have it setup like:
keyletter = "a"
keyletter2 = "b"
etc.
Loop problem picture
The Problem is with the statement
//if (keyLetter2 == crossword[i-1, j])
Here, if the matrix is like
"
a b c
d e f
g h i
"
and suppose you want to search for pattern "ba", Then obviously you will look for b, hence i will be 0 and j will be 1. Now, you are going to do (i-1) which will be -1(there are no negative indexes), Hence the error.
The best approach to do this is to check if i or j are already 0 or not. If they are 0, then you don't need a i-1 or j-1 and you can go like
" if(i!=0)
then
if (keyLetter2 == crossword[i-1, j])
...
"
I can't see any infinite loop, but run time exceptions (IndexOutOfRangeException) at
if (keyLetter2 == crossword[i-1, j]) {...} // if i == 0
...
else if (keyLetter2 == crossword[i+1, j]) {...} // if i == crossword.GetLength(0) - 1
Let's get rid of these exceptions and brush up the loop:
for (int i = 0; i < crossword.GetLength(0); ++i)
for (int j = 0; j < crossword.GetLength(1); ++j)
if (keyLetter == crossword[i, j]) {
// Keep you messages being readable with a help of string interpolation - $""
Console.WriteLine(string.Join(Environment.NewLine,
$"{keyLetter} is [{i}, {j}]",
""
$"i is: {i}",
""
$"j is: {j}",
""
));
// Do not repeat yourself: if you want 4 neighbors to test
for (int neighbor = 0; neighbor < 4; ++neighbor) {
int ii = i + (neighbor % 2) * (neighbor - 1);
int jj = j + (1 - neighbor % 2) * (neighbor - 1);
// Check indexes ii, jj before addressing [ii, jj]
if (ii >= 0 && ii < crossword.GetLength(0) &&
jj >= 0 && jj < crossword.GetLength(1) &&
keyLetter2 == crossword[ii, jj]) {
Console.Write($"[i/j] position for {keyLetter2} is [{i}, {j}]");
// In case we want at most one neighbor; comment it out if we want all of them
break;
}
}
}
In case you have 8 (not 4) neighbors to check
...
bool found = false;
for (int ii = Math.Max(0, i - 1); ii <= Math.Min(i + 1, crossword.GetLength(0)) && !found; ++ii)
for (int jj = Math.Max(0, j - 1); jj <= Math.Min(j + 1, crossword.GetLength(0)) && !found; ++jj) {
if ((ii != i || jj != j) && keyLetter2 == crossword[ii, jj])) {
Console.Write($"[i/j] position for {keyLetter2} is [{i}, {j}]");
// In case we want at most one neighbor; comment it out if we want all of them
found = true;
}
}
I would take a slightly different approach, which would be to delegate the "neighbor search" to a helper method. You can pass to this method the array, the cell whose neighbors should be searched, and the value that you're searching for.
Because a cell item is defined by two integer coordinates, and because there's an existing Point structure we can use that has two integer properties (X and Y), I'm using that to represent a cell in our array.
The helper function works by determining the minimum value of X and Y around the cell whose neighbors we're searching by subtracting 1 from it's X and Y values. Then we need to make sure this result is not less than 0 to ensure we stay within the bounds of the array.
Likewise, we add 1 to get the maximum value, and make sure it's not greater than the upper bound of the array.
Finally, we return the list of matches (if any were found) in a List<Point>:
public static List<Point> GetNeighborMatches(string[,] grid, Point item, string valueToFind)
{
var result = new List<Point>();
// if our grid is empty or the item isn't in it, return an empty list
if (grid == null || grid.Length == 0 ||
item.X < 0 || item.X > grid.GetUpperBound(0) ||
item.Y < 0 || item.Y > grid.GetUpperBound(1))
{
return result;
}
// Get min and max values of x and y for searching
var minX = Math.Max(item.X - 1, 0);
var maxX = Math.Min(item.X + 1, grid.GetUpperBound(0));
var minY = Math.Max(item.Y - 1, 0);
var maxY = Math.Min(item.Y + 1, grid.GetUpperBound(1));
// Loop through all neighbors to find a match
for (int x = minX; x <= maxX; x++)
{
for (int y = minY; y <= maxY; y++)
{
// Continue looping if we're on the 'item'
if (x == item.X && y == item.Y) continue;
// If this is a match, add it to our list
if (grid[x, y] == valueToFind) result.Add(new Point(x, y));
}
}
// Return all the matching neighbors
return result;
}
Now we can use this helper method inside your existing code:
public static void Main(string[] args)
{
string keyLetter = "g";
string keyLetter2 = "b";
string[,] crossword = new string[,]
{
{ "a", "b", "c", "d" },
{ "e", "f", "g", "h" },
{ "a", "e", "b", "c" },
{ "i", "j", "k", "l" }
};
for (int i = 0; i < crossword.GetLength(0); i++)
{
for (int j = 0; j < crossword.GetLength(1); j++)
{
if (keyLetter == crossword[i, j])
{
// we found a match for our first letter!
Console.WriteLine("The first value (" + keyLetter + ") is at [" +
i + ", " + j + "]");
// get neighboring matches for keyLetter2
List<Point> matches = GetNeighborMatches(crossword,
new Point(i, j), keyLetter2);
// Output our matches if we found any
if (matches.Any())
{
foreach (var match in matches)
{
Console.WriteLine("The second value (" + keyLetter2 +
") is at [" + match.X + ", " + match.Y + "]");
}
}
else
{
Console.WriteLine("No match was found for '" + keyLetter2 +
"' near [" + i + ", " + j + "]");
}
}
}
}
}
Output
I have a list of items that are determined by what a player enters into several input fields, but for the sake of this example, let's say that it contains "a", "b", "c", "d", and "e". These are then sorted into a dictionary with a list of numbers as the values (unimportant). I then randomize the dictionary with two different randomizer variables (i and j), for the sake of taking two objects from the dictionary and displaying them to the screen, so the player can press various associated buttons. This goes on until x amount of turns have passed. The main problem I'm having is the prevention of semi-duplicates, such as "a b" and "b a" from appearing.
I have tried entering inserting both the randomized pair and its semi-duplicate into another dictionary and then using while loop statements preventing any pairs from that dictionary from appearing. Unfortunately, this hasn't worked.
Below is my code.
public void Start() {
finalList = new Dictionary<string, int>();
for (index = 0; index < allNumbers; index++)
{
finalList.Add(itemList[index], valueList[index]);
Debug.Log(finalList[index.ToString()]);
}
}
public void Update() {
choose();
}
public void choose() {
duplicates = new Dictionary<string, string>();
duplicates.Clear();
while (rounds < insertNum) {
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
while (j == i || (duplicates.ContainsKey(key) || duplicates.ContainsKey(reverseKey))) {
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
//break;
} while (j == i || (duplicates.ContainsKey(key) && duplicates.ContainsKey(reverseKey)))
{
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
}while (j == i && (duplicates.ContainsKey(key) || dupes.ContainsKey(reverseKey))) {
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
}
while (j == i && (duplicates.ContainsKey(key) && duplicates.ContainsKey(reverseKey))) {
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
}
duplicates.Add(key, "1"); // the one is just a filler variable
duplicates.Add(reverseKey, "1");
if (buttonOneBool) { //this is in another script, ignore
finalList[itemList[i].ToString()] = valueList[i] += 2;
finalList[itemList[j].ToString()] = valueList[j] -= 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonTwoBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 2;
finalList[itemList[j].ToString()] = valueList[j] += 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonThreeBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 1;
finalList[itemList[j].ToString()] = valueList[j] -= 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonFourBool) {
finalList[itemList[i].ToString()] = valueList[i] += 1;
finalList[itemList[j].ToString()] = valueList[j] += 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
}
break;
}
The simplest way to fix this is to guarantee i < j. When selecting a new i and j like this:
i = UnityEngine.Random.Range(min, max);
j = UnityEngine.Random.Range(min, max);
Instead do this:
i = UnityEngine.Random.Range(min, max - 1);
j = UnityEngine.Random.Range(i + 1, max);
Doing this rules out the possibility of selecting a "mirror image" of a previous case and also avoids the "i == j" case.
After these modifications your choose() function should look something like this:
public void choose()
{
duplicates = new HashSet<string>();
while (rounds < insertNum)
{
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
while (duplicates.Contains(key))
{
i = UnityEngine.Random.Range(0, allNumbers - 2);
j = UnityEngine.Random.Range(i + 1, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
}
duplicates.Add(key); // the one is just a filler variable
if (buttonOneBool) { //bool definitions are in another script, ignore
finalList[itemList[i].ToString()] = valueList[i] += 2;
finalList[itemList[j].ToString()] = valueList[j] -= 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonTwoBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 2;
finalList[itemList[j].ToString()] = valueList[j] += 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonThreeBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 1;
finalList[itemList[j].ToString()] = valueList[j] -= 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonFourBool) {
finalList[itemList[i].ToString()] = valueList[i] += 1;
finalList[itemList[j].ToString()] = valueList[j] += 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
}
break;
}
}
You can get two random indexes to a list using code below. The code uses integer as the list type and will work with any type.
List<int> randomList = itemList.Select((x, i) => new { index = i, rand = rand.Next() }).OrderBy(x => x.rand).Select(x => x.index).ToList();
Then the two random indexes into the list are randomList[0] and randomList[1]. The code assigns a random number to each index i of the list. To get the two items from the list use itemList[randomList[0]] and itemList[randomList[1]]. The code assumes there are at least two items in the original list ItemList[].
If your original list has duplicates than you need to use Distinct() as shown in code below
List<int> distinctList = itemList.Distinct().ToList();
List<int> randomList = distinctList.Select((x, i) => new { index = i, rand = rand.Next() }).OrderBy(x => x.rand).Select(x => x.index).ToList();
I am new to c# and specifically classes. I am trying to have multiple dice roll and for the top line of dice be the players dice and the bottom the computer dice. I set up a counter to count each dice roll and tell the user. So far it gives me 0.
Also the dice rolls are meant to be random but I am not sure they are completly random.
Any help would be great. Thank you.
namespace Week_6._3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
public void Squares(int x, int y)
{
int width = this.pictureBox1.Width;
int sizeDice = width/ 7;
Graphics paper = pictureBox1.CreateGraphics();
SolidBrush brushWhite = new SolidBrush(Color.White);
Pen penBlack = new Pen(Color.Black);
paper.FillRectangle(brushWhite, x, y, sizeDice, sizeDice);
paper.DrawRectangle(penBlack, x, y, sizeDice, sizeDice);
}
public void Circles(int x, int y, int counter)
{
SolidBrush blackbrush = new SolidBrush(Color.Black);
Graphics paper = pictureBox1.CreateGraphics();
int width = this.pictureBox1.Width;
int length = this.pictureBox1.Height;
int sizeDice = width / 7;
int sizeCircle = sizeDice/7;
int firstRowCircles = sizeCircle;
int secondRowCircles = sizeCircle * 3;
int thirdRowCircles = sizeCircle * 5;
Random random = new Random();
int num = random.Next(1,6);
if (num == 1)
{
counter = counter + 1;
paper.FillEllipse(blackbrush, x + secondRowCircles, y + secondRowCircles, sizeCircle, sizeCircle);
}
else if (num == 2)
{
counter = counter + 2;
paper.FillEllipse(blackbrush, x + firstRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
}
else if (num == 3)
{
counter = counter + 3;
paper.FillEllipse(blackbrush, x + firstRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + secondRowCircles, y + secondRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
}
else if (num == 4)
{
counter = counter + 4;
paper.FillEllipse(blackbrush, x + firstRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + firstRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
}
else if (num == 5)
{
counter = counter + 5;
paper.FillEllipse(blackbrush, x + firstRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + firstRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + secondRowCircles, y + secondRowCircles, sizeCircle, sizeCircle);
}
else if (num == 6)
{
counter = counter + 6;
paper.FillEllipse(blackbrush, x + firstRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + firstRowCircles, y + secondRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + firstRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + firstRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + secondRowCircles, sizeCircle, sizeCircle);
paper.FillEllipse(blackbrush, x + thirdRowCircles, y + thirdRowCircles, sizeCircle, sizeCircle);
}
}
private void buttonRollDice_Click(object sender, EventArgs e)
{
int width = this.pictureBox1.Width;
int length = this.pictureBox1.Height;
int widthShift = width / 7;
int middleWidth = widthShift * 3;
int rightWidth = widthShift * 5;
int allTopDiceLength = length / 8;
int allBottomDiceLength = allTopDiceLength + (length / 2);
int playerCounter = 0;
int computerCounter = 0;
//Top Left Square
Squares(widthShift, allTopDiceLength);
Circles(widthShift, allTopDiceLength, playerCounter);
//Bottom Left Square
Squares(widthShift, allBottomDiceLength);
Circles(widthShift, allBottomDiceLength, computerCounter);
//Top Middle Square
Squares(middleWidth, allTopDiceLength);
Circles(middleWidth, allTopDiceLength, playerCounter);
//Bottom Middle Square
Squares(middleWidth, allBottomDiceLength);
Circles(middleWidth, allBottomDiceLength, computerCounter);
//Top Right Square
Squares(rightWidth, allTopDiceLength);
Circles(rightWidth, allTopDiceLength, playerCounter);
//Bottom Middle Square
Squares(rightWidth, allBottomDiceLength);
Circles(rightWidth, allBottomDiceLength, computerCounter);
labelPlayerRolls.Text = Convert.ToString(playerCounter);
}
}
}
Code is actually updating function argument, so you will not see updated value in caller method/function as it is passed by value not reference.
You have couple of options to fix this problem.
Option 1:
You could modify circle method definition to have return value.
public int Circles(int x, int y, int counter)
{
...
return counter
}
Option 2:
Pass counter as reference argument.
public void Circles(int x, int y, ref int counter)
{
...
}
Option 3:
Define instance variable and update the value as required(no need to pass argument, directly use instance variable).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int counter;
....
public void Circles(int x, int y)
{
...
}
}
I'm writing a Word search puzzle in c# and I'm almost done, only missing one thing.
I have a char[,] matrix that I fill up with words I read from a txt file, and if there's no available places for a word left, I fill up the remaining spots with random letters. My problem is that I don't know how to make words that contains the some letter cross eachother. Here's my code for the word placing:
private bool PlaceWord(string word, int _row, int _col, int x, int y)
{
if(x > 0)
{
if (_row + word.Length > row)
return false;
}
if(x < 0)
{
if (_row - word.Length < 0)
return false;
}
if(y > 0)
{
if (_col + word.Length > col)
return false;
}
if(y < 0)
{
if (_col - word.Length < 0)
return false;
}
for(int i = 0; i < word.Length; i++)
{
if (matrix[(i * x) + _row, (i * y) + _col] != ' ')
return false;
}
for (int i = 0; i < word.Length; i++)
{
matrix[(i * x) + _row, (i * y) + _col] = word[i];
answers[(i * x) + _row, (i * y) + _col] = word[i];
}
return true;
}
In your first for-loop
if ((matrix[i*x+_row, i*y+_col] != ' ') && (matrix[i*x+_row, i*y+_col] != word[i]))
return false
I'm writing a little library and want to be able to sum an array.
A singlethread version works fine, but when I add multithreading, everything breaks.
I'm using a partitioner to split data on blocks and then sum every part in single result. Then I return it. But data is invalid, it's doesn't seems that there is any race condition, because every program reboot leads to same results.
[Pure]
public static double Sum(this double[] source)
{
source.IsNotNull("source");
if (source.Length < Constants.SingleThreadExecutionThreshold)
return Sum(source, 0, source.Length);
double result = 0;
object syncRoot = new object();
Parallel.ForEach(Partitioner.Create(0, source.Length),
() => (double)0,
(range, state, sum) => Sum(source, range.Item1, range.Item2),
x =>
{
lock (syncRoot)
result += x;
});
return result;
}
Sum(source, from, to) always give correct results. Here is an implementation:
[Pure]
private static double Sum(this double[] source, int startIndex, int endIndex)
{
double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
checked
{
int i;
for (i = startIndex; i < endIndex - Constants.Step + 1; i += Constants.Step)
{
sum1 += source[i];
sum2 += source[i + 1];
sum3 += source[i + 2];
sum4 += source[i + 3];
}
if (i == source.Length)
return ((sum1 + sum2) + (sum3 + sum4));
if (i == source.Length - 1)
return ((sum1 + sum2) + (sum3 + sum4) + source[i]);
if (i == source.Length - 2)
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1]));
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1] + source[i + 2]));
}
}
internal static class Constants
{
public const int Step = 4;
public const int SingleThreadExecutionThreshold = 1024;
}
How can it be fixed?
Code example: http://ideone.com/8sD0JL
Okay, think I've fixed it. I've found two major bugs.
the array.Length thing
you were misusing the "finally" delegate. There is no guarantee that that code ever gets run
With these changes I get a difference of -0.000576496124267578, which is within expected for double sum rounding errors.
[Pure]
public static double Sum(this double[] source, int startIndex, int endIndex)
{
double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
checked
{
int i;
int j = 0;
for (i = startIndex; i < endIndex - Constants.Step + 1; i += Constants.Step)
{
sum1 += source[i];
sum2 += source[i + 1];
sum3 += source[i + 2];
sum4 += source[i + 3];
j += Constants.Step;
}
var segmentLength = endIndex - startIndex;
if (j == segmentLength)
return ((sum1 + sum2) + (sum3 + sum4));
if (j == segmentLength - 1)
return ((sum1 + sum2) + (sum3 + sum4) + source[i]);
if (j == segmentLength - 2)
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1]));
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1] + source[i + 2]));
}
}
internal static class Constants
{
public const int Step = 4;
public const int SingleThreadExecutionThreshold = 1024;
}
[Pure]
public static double Sum(this double[] source)
{
if (source.Length < Constants.SingleThreadExecutionThreshold)
return Sum(source, 0, source.Length);
double result = 0;
object syncRoot = new object();
Parallel.ForEach(Partitioner.Create(0, source.Length),
(range) => {
var x = Sum(source, range.Item1, range.Item2);
lock (syncRoot)
result += x;
});
return result;
}