There is something wrong with the comparison of >= method:
public static bool operator >=(Fraction left, Fraction right)
{
return left.CompareTo(right) >= 0;
}
It doesn't seem to work?!
For example:
25 >= 6/5 returns FALSE
Can anyone shed some light into this?
Below is the requested code. I hope somebody can spot what's wrong with it.
/// <summary>
/// Compares an object to this Fraction
/// </summary>
/// <param name="obj">The object to compare against (null is less than everything)</param>
/// <returns>-1 if this is less than <paramref name="obj"></paramref>,
/// 0 if they are equal,
/// 1 if this is greater than <paramref name="obj"></paramref></returns>
/// <remarks>Will convert an object from longs, doubles, and strings as this is a value-type.</remarks>
public int CompareTo(object obj)
{
if (obj == null)
return 1; // null is less than anything
Fraction right;
if (obj is Fraction)
right = (Fraction)obj;
else if (obj is long)
right = (long)obj;
else if (obj is double)
right = (double)obj;
else if (obj is string)
right = (string)obj;
else
throw new ArgumentException("Must be convertible to Fraction", "obj");
return this.CompareTo(right);
}
/// <summary>
/// Compares this Fraction to another Fraction
/// </summary>
/// <param name="right">The Fraction to compare against</param>
/// <returns>-1 if this is less than <paramref name="right"></paramref>,
/// 0 if they are equal,
/// 1 if this is greater than <paramref name="right"></paramref></returns>
public int CompareTo(Fraction right)
{
// if left is an indeterminate, punt to the helper...
if (this.m_Denominator == 0)
{
return IndeterminantCompare(NormalizeIndeterminate(this.m_Numerator), right);
}
// if right is an indeterminate, punt to the helper...
if (right.m_Denominator == 0)
{
// note sign-flip...
return -IndeterminantCompare(NormalizeIndeterminate(right.m_Numerator), this);
}
// they're both normal Fractions
CrossReducePair(ref this, ref right);
try
{
checked
{
long leftScale = this.m_Numerator * right.m_Denominator;
long rightScale = this.m_Denominator * right.m_Numerator;
if (leftScale < rightScale)
return -1;
else if (leftScale > rightScale)
return 1;
else
return 0;
}
}
catch (Exception e)
{
throw new FractionException(string.Format("CompareTo({0}, {1}) error", this, right), e);
}
}
/// <summary>
/// Cross-reduces a pair of Fractions so that we have the best GCD-reduced values for multiplication
/// </summary>
/// <param name="frac1">The first Fraction [WILL BE MODIFIED IN PLACE]</param>
/// <param name="frac2">The second Fraction [WILL BE MODIFIED IN PLACE]</param>
/// <remarks>Modifies the input arguments in-place!</remarks>
/// <example>(3/4, 5/9) = (1/4, 5/3)</example>
public static void CrossReducePair(ref Fraction frac1, ref Fraction frac2)
{
// leave the indeterminates alone!
if (frac1.m_Denominator == 0 || frac2.m_Denominator == 0)
return;
long gcdTop = GCD(frac1.m_Numerator, frac2.m_Denominator);
frac1.m_Numerator = frac1.m_Numerator / gcdTop;
frac2.m_Denominator = frac2.m_Denominator / gcdTop;
long gcdBottom = GCD(frac1.m_Denominator, frac2.m_Numerator);
frac2.m_Numerator = frac2.m_Numerator / gcdBottom;
frac1.m_Denominator = frac1.m_Denominator / gcdBottom;
}
CrossReducePair changes the relationship between the numbers. Your example of (3/4, 5/9) = (1/4, 5/3) makes it very obvious. Cross reducing makes sense if you're multiplying, but not if you're just comparing them.
The CrossReducePair method doesn't seem to make much sense. As noted in the comments, it transforms (3/4, 5/9) to (1/4, 5/3). Note: 3/4 > 5/9, but 1/4 < 5/3.
Also, it's a very bad idea to have a CompareTo method which modifies the object.
Related
I've started working on Sort Algorithms and got Quicksort working with the Activation Method as the first index for the pivot.
Now, if i try to use either a random index or the the last one it either just doesn't do anything or it just sorts the last half. A Video Showing what i mean.
My Code:
class Quicksort : Sortalgorithm
{
int ActivationMethod = -1;
Random rand = new Random();
/// <summary>
/// The Constructor for the Quicksort Algorithm
/// </summary>
/// <param name="arr">The Array to sort</param>
/// <param name="Actv">Activation Method: 0=First; 1=Last; 2=Random</param>
/// <param name="del">The Delay for the Algorithm between each sort</param>
/// <param name="sound">Wether Sound should be played</param>
/// <param name="gui">The Instance of the GUI</param>
public Quicksort(int[] arr, int Actv, int del, bool sound, gui gui) : base(del, sound, gui)
{
if (arr.Length < 5)
{
throw new Exception("Array has too few Entries");
}
ActivationMethod = Actv;
toSort = arr; // Is A Global Array from the inherited Class
}
protected override int sort()
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
return qsort(getPivot(), toSort.Length-1);
}
/// <summary>
/// The Quicksort Recursive Logic. Both Params are needed for the "sub-array"
/// </summary>
/// <param name="start">StartIndex for the Current Array</param>
/// <param name="end">EndIndex for the Current Array</param>
/// <returns></returns>
private int qsort(int start, int end)
{
if (start < end)
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
int pivot = partition(start, end); //get the pivot
qsort(start, pivot); // do the same for the frist part (smaller or equal to pivot)
qsort(pivot + 1, end); //and now for the second part (the largen than pivot)
}
return 1;
}
/// <summary>
/// The Partitioning Logic for The Array. Both Params are needed for the "sub-array"
/// </summary>
/// <param name="start">StartIndex for the Current Array</param>
/// <param name="end">EndIndex for the Current Array</param>
/// <returns></returns>
private int partition(int start, int end)
{
int pivot = toSort[start]; //Depending on Activiation Method
int swapIndex = start;
for (int i = start + 1; i <= end; i++)
{
if (toSort[i] < pivot) //Sort the Index Accrodingly
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
swapIndex++;
swap(i, swapIndex); // Is A Method inherited from a Class
}
}
swap(start, swapIndex);
return swapIndex;
}
/// <summary>
/// Get The Pivot returning on the Declared Activation Method in the Construcor
/// </summary>
/// <returns>PivotIndex</returns>
private int getPivot()
{
switch (ActivationMethod)
{
case 0:
return 0;
case 1:
return toSort.Length - 1;
case 2:
return rand.Next(0, toSort.Length - 1);
default:
throw new Exception("No Activationmethod Defined");
};
}
}
I've been Stuck for quite a while with this problem, any help would be appreciated
There's a flaw in your logic.
You do qsort(getPivot(), toSort.Length-1);
at the top level to select the pivot, and then in partition() you do
int pivot = toSort[start];
That's not going to work: the first argument to qsort is where to start sorting, so anything but zero here at the top level will prevent parts of the input from participating in the sort.
Instead the pivot needs to be selected for each partition, so that must take place inside partition():
private int partition(int start, int end)
{
int pivot = toSort[getPivot(start, end)]; //Depending on Activiation Method
And then getPivot() must be modified to take the start and end arguments into account when calculating the pivot index to be within the proper range, based on the desired pivot selection method.
I want to implement a search and replace algorithm on a stream, where I would create a stream class that would take a source stream as input, and who's read method would read from the source stream as needed and perform a search/replace as the stream is being read. This would mitigate the entire set of data from being loaded into memory at once and enable search & replace on very large data sets.
To do this I thought I would start with an existing search algorithm, adapt it to a stream approach, then adapt the replace functionality.
Below is a Knuth Morris Pratt implementation I adapted from an online example.
Has anybody adapted something like this to a stream approach? I would need to take into account searching across read boundaries, which I'm not sure how I would do yet.
/// <summary>
/// The KMP matching algorithm uses degenerating property (SearchPattern having same sub-SearchPatterns appearing more than once in the SearchPattern)
/// of the SearchPattern and improves the worst case complexity to O(n). The basic idea behind KMP’s algorithm is: whenever we
/// detect a mismatch (after some matches), we already know some of the characters in the text of the next window. We take
/// advantage of this information to avoid matching the characters that we know will anyway match.
/// </summary>
/// <seealso cref="https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/"/>
public class KMPSearch
{
/// <summary>
/// Pattern we are looking for
/// </summary>
readonly string sSearchPattern_m;
/// <summary>
/// Text we are looking in
/// </summary>
readonly string sData_m;
/// <summary>
/// Index for text
/// </summary>
private int iDataPosition_m;
private int iDataLength_m;
/// <summary>
/// Index for search pattern
/// </summary>
private int iSearchPatternPosition_m;
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// </summary>
readonly int[] lstLongestProperPrefix_m;
public KMPSearch(string sSearchPattern, string sData)
{
this.sSearchPattern_m = sSearchPattern;
this.sData_m = sData;
this.iDataLength_m = sData.Length;
// create lps that will hold the longest prefix suffix values for SearchPattern
this.lstLongestProperPrefix_m = new int[sSearchPattern.Length];
// Pre-process the SearchPattern (calculate lps array)
this.ComputeLPSArray();
this.iDataPosition_m = 0; // index for txt
this.iSearchPatternPosition_m = 0; // index for pat
}
/// <summary>
/// Find next match
/// </summary>
/// <returns></returns>
public int Next()
{
int iMatchIndex = -1;
//We start comparison of pat[iSearchPatternPosition_m] with iSearchPatternPosition_m = 0 with characters of current window of text.
//We keep matching characters txt[iDataPosition_m] and pat[iSearchPatternPosition_m] and keep incrementing iDataPosition_m and iSearchPatternPosition_m while
//pat[iSearchPatternPosition_m] and txt[iDataPosition_m] keep matching.
while (iDataPosition_m < this.iDataLength_m)
{
if (this.sSearchPattern_m[iSearchPatternPosition_m] == this.sData_m[iDataPosition_m])
{
iSearchPatternPosition_m++;
iDataPosition_m++;
}
if (iSearchPatternPosition_m == sSearchPattern_m.Length)
{
//Console.WriteLine("Found SearchPattern at index %d ", iDataPosition_m - iSearchPatternPosition_m);
iMatchIndex = iDataPosition_m - iSearchPatternPosition_m;
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
// mismatch after j matches
else if (iDataPosition_m < this.iDataLength_m && this.sSearchPattern_m[iSearchPatternPosition_m] != this.sData_m[iDataPosition_m])
{
//When we see a mismatch
//* We know that characters pat[0..iSearchPatternPosition_m - 1] match with txt[iDataPosition_m-iSearchPatternPosition_m..iDataPosition_m - 1]
// (Note that iSearchPatternPosition_m starts with 0 and increment it only when there is a match).
//* We also know (from above definition) that lps[iSearchPatternPosition_m - 1] is count of characters of pat[0…iSearchPatternPosition_m - 1]
// that are both proper prefix and suffix.
//* From above two points, we can conclude that we do not need to match these lps[iSearchPatternPosition_m - 1] characters with
// txt[iDataPosition_m -iSearchPatternPosition_m..iDataPosition_m - 1] because we know that
// these characters will anyway match. Let us consider above example to understand this.
// Do not match lps[0..lps[iSearchPatternPosition_m - 1]] characters,
// they will match anyway
if (iSearchPatternPosition_m != 0)
{
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
else
{
iDataPosition_m = iDataPosition_m + 1;
}
}
if (iMatchIndex > -1)
{
return iMatchIndex;
}
}
return iMatchIndex;
}
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// Fills lps for given pattern pat[0..M-1]
/// lps[i] = the longest proper prefix of pat[0..i] which is also a suffix of pat[0..i].
/// </summary>
private void ComputeLPSArray()
{
// length of the previous longest prefix suffix
int len = 0;
this.lstLongestProperPrefix_m[0] = 0; // lps[0] is always 0
// the loop calculates lps[i] for i = 1 to M-1
int i = 1;
while (i < this.sSearchPattern_m.Length)
{
if (this.sSearchPattern_m[i] == this.sSearchPattern_m[len])
{
len++;
this.lstLongestProperPrefix_m[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
// This is tricky. Consider the example.
// AAACAAAA and i = 7. The idea is similar
// to search step.
if (len != 0)
{
len = this.lstLongestProperPrefix_m[len - 1];
// Also, note that we do not increment
// i here
}
else // if (len == 0)
{
this.lstLongestProperPrefix_m[i] = 0;
i++;
}
}
}
}
}
Then I took the above algorithm and retrofitted it into a Stream. As a proof of concept, the stream will raise an event during it's read method every time it finds the search pattern. It currently has a limitation of not being able to search across the read boundaries. So if 1024 bytes are read at a time, and the length of the source stream is 2048 bytes, two reads are executed to read the entire stream. The issue is that if the search pattern starts at index 1000 and is 40 bytes long, it will not be found. I think once this issue is solved, the actual replace functionality won't be that difficult. I'm looking for suggestions on how to implement the search across read boundaries. It probably involves caching part of the previous read. Is anybody aware of a streaming implementation similar to this or suggestions?
public class KMPSearchStream : Stream
{
public override bool CanRead { get { return true; } }
public override bool CanSeek => throw new NotImplementedException();
public override bool CanWrite => throw new NotSupportedException();
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public class PatternFoundEventArgs
{
public int Index { get; internal set; }
}
public delegate void PatternFoundEvent(PatternFoundEventArgs e);
private Stream strSource_m;
/// <summary>
/// Pattern we are looking for
/// </summary>
private byte[] searchPattern_m;
/// <summary>
/// Text we are looking in
/// </summary>
private byte[] data_m;
/// <summary>
/// Index for text
/// </summary>
private int iDataPosition_m;
private int iDataLength_m;
/// <summary>
/// Index for search pattern
/// </summary>
private int iSearchPatternPosition_m;
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// </summary>
readonly int[] lstLongestProperPrefix_m;
public KMPSearchStream(Stream strSource, byte[] searchPattern)
{
if (strSource == null)
{
throw new ArgumentNullException(nameof(strSource), "Source stream is null.");
}
if (searchPattern == null || searchPattern.Length == 0)
{
throw new ArgumentNullException(nameof(searchPattern), "Pattern to find is null or empty.");
}
this.strSource_m = strSource;
this.searchPattern_m = searchPattern;
// create lps that will hold the longest prefix suffix values for SearchPattern
this.lstLongestProperPrefix_m = new int[searchPattern.Length];
// Pre-process the SearchPattern (calculate lps array)
this.ComputeLPSArray();
this.iDataPosition_m = 0; // index for txt
this.iSearchPatternPosition_m = 0; // index for pat
}
public event PatternFoundEvent OnPatternFound;
public override void Flush()
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
int iRead = this.strSource_m.Read(buffer, offset, count);
this.iDataPosition_m = 0; // index for txt
this.iSearchPatternPosition_m = 0; // index for pat
this.data_m = buffer;
this.iDataPosition_m = offset;
this.iDataLength_m = iRead;
int iIndex;
while ((iIndex = this.Next()) > -1)
{
this.OnPatternFound(new PatternFoundEventArgs()
{
Index = iIndex
});
}
return iRead;
}
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// Fills lps for given pattern pat[0..M-1]
/// lps[i] = the longest proper prefix of pat[0..i] which is also a suffix of pat[0..i].
/// </summary>
private void ComputeLPSArray()
{
// length of the previous longest prefix suffix
int len = 0;
this.lstLongestProperPrefix_m[0] = 0; // lps[0] is always 0
// the loop calculates lps[i] for i = 1 to M-1
int i = 1;
while (i < this.searchPattern_m.Length)
{
if (this.searchPattern_m[i] == this.searchPattern_m[len])
{
len++;
this.lstLongestProperPrefix_m[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
// This is tricky. Consider the example.
// AAACAAAA and i = 7. The idea is similar
// to search step.
if (len != 0)
{
len = this.lstLongestProperPrefix_m[len - 1];
// Also, note that we do not increment
// i here
}
else // if (len == 0)
{
this.lstLongestProperPrefix_m[i] = 0;
i++;
}
}
}
}
/// <summary>
/// Find next match
/// </summary>
/// <returns></returns>
public int Next()
{
int iMatchIndex = -1;
//We start comparison of pat[iSearchPatternPosition_m] with iSearchPatternPosition_m = 0 with characters of current window of text.
//We keep matching characters txt[iDataPosition_m] and pat[iSearchPatternPosition_m] and keep incrementing iDataPosition_m and iSearchPatternPosition_m while
//pat[iSearchPatternPosition_m] and txt[iDataPosition_m] keep matching.
while (iDataPosition_m < this.iDataLength_m)
{
if (this.searchPattern_m[iSearchPatternPosition_m] == this.data_m[iDataPosition_m])
{
iSearchPatternPosition_m++;
iDataPosition_m++;
}
if (iSearchPatternPosition_m == searchPattern_m.Length)
{
//Console.WriteLine("Found SearchPattern at index %d ", iDataPosition_m - iSearchPatternPosition_m);
iMatchIndex = iDataPosition_m - iSearchPatternPosition_m;
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
// mismatch after j matches
else if (iDataPosition_m < this.iDataLength_m && this.searchPattern_m[iSearchPatternPosition_m] != this.data_m[iDataPosition_m])
{
//When we see a mismatch
//* We know that characters pat[0..iSearchPatternPosition_m - 1] match with txt[iDataPosition_m-iSearchPatternPosition_m..iDataPosition_m - 1]
// (Note that iSearchPatternPosition_m starts with 0 and increment it only when there is a match).
//* We also know (from above definition) that lps[iSearchPatternPosition_m - 1] is count of characters of pat[0…iSearchPatternPosition_m - 1]
// that are both proper prefix and suffix.
//* From above two points, we can conclude that we do not need to match these lps[iSearchPatternPosition_m - 1] characters with
// txt[iDataPosition_m -iSearchPatternPosition_m..iDataPosition_m - 1] because we know that
// these characters will anyway match. Let us consider above example to understand this.
// Do not match lps[0..lps[iSearchPatternPosition_m - 1]] characters,
// they will match anyway
if (iSearchPatternPosition_m != 0)
{
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
else
{
iDataPosition_m = iDataPosition_m + 1;
}
}
if (iMatchIndex > -1)
{
return iMatchIndex;
}
}
return iMatchIndex;
}
}
If I understand you correctly, the goal is to scan an ongoing stream and replace all occurences of a mask with a replacement value before forwarding it to an output stream.
If so, there are a few requirements to the system:
Only data may be forwarded for which can be proven that it is not part of a complete mask
Data must be forwarded as soon as possible to keep congestion to a minimum
Abstract:
In essence, you buffer all the data before you forward it.
After each character, you test whether the buffer could be part of the mask.
If it couldn't be part of the mask, you release (write to output) one character after the other from the buffer until either the buffer is empty, or the buffer could match the mask again.
Code:
I made a helper class that does essentially that.
I don't know whether it implements your algorithm, this is just something I cooked up.
According to my tests, it works, but I can't guarantee anything.
You can use it in your stream class and just forward the calls and recive the callbacks.
Forward input data via the Write([...]) methods, get output via the output callback and define the mask and replacement string. It supports flushing and reseting.
using System;
using System.Collections.Generic;
/// <summary>
/// Class that handles replacement of string occurrences that match a mask in a string stream.
/// </summary>
public class StreamReplacement
{
private Action<char> output;
private string mask;
private string replacement;
// Buffer for unverified characters
private readonly List<char> buffer;
// Current index on the mask for the next character added
private int maskPosition;
/// <summary>
/// All verified characters will be passed to the output
/// </summary>
/// <exception cref="System.ArgumentNullException">The output cannot be null.</exception>
public Action<char> Output
{
get => output;
set => output = value ?? throw new ArgumentNullException();
}
/// <summary>
/// Gets or sets the mask to test for.
/// </summary>
/// <exception cref="System.ArgumentException">The mask must contain at least one character.</exception>
public string Mask
{
get => this.mask;
set
{
if (string.IsNullOrEmpty(mask))
{
throw new ArgumentException("The mask must contain at least one character.");
}
this.mask = value;
// Clean buffer and throw out characters that can't be part of the mask anymore.
WriteVerifiedCharactersFromBuffer();
}
}
/// <summary>
/// Gets or sets the replacement string to output when the mask has been encountered.
/// </summary>
public string Replacement
{
get => replacement;
set => replacement = value ?? string.Empty;
}
/// <summary>
/// Initializes a new instance of the <see cref="StreamReplacement"/> class.
/// </summary>
/// <param name="output">The output.</param>
/// <param name="mask">The mask.</param>
/// <param name="replacement">The replacement string for mask encounters.</param>
/// <exception cref="System.ArgumentNullException">Output cannot be null.</exception>
/// <exception cref="System.ArgumentException">The mask must contain at least one character.</exception>
public StreamReplacement(Action<char> output, string mask, string replacement)
{
this.output = output ?? throw new ArgumentNullException(nameof(output));
this.mask = string.IsNullOrEmpty(mask) ? throw new ArgumentException("The mask must contain at least one character.") : mask;
this.replacement = replacement ?? string.Empty;
this.buffer = new List<char>(mask.Length);
}
/// <summary>
/// Writes a single character to the stream.
/// </summary>
public void Write(char c)
{
WriteCharacter(c);
}
/// <summary>
/// Writes the specified text to the stream.
/// </summary>
public void Write(string text)
{
if (string.IsNullOrEmpty(text))
{
return;
}
foreach (var c in text)
{
WriteCharacter(c);
}
}
/// <summary>
/// Writes the specified characters to the stream.
/// </summary>
/// <param name="characters">The characters.</param>
public void Write(params char[] characters)
{
if (characters == null)
{
return;
}
foreach (var c in characters)
{
WriteCharacter(c);
}
}
/// <summary>
/// Flushes all buffered characters, even if they could be part of the mask.<br/>
/// Starts from scratch after flushing.
/// </summary>
public void Flush()
{
foreach (var c in buffer)
{
output(c);
}
buffer.Clear();
maskPosition = 0;
}
/// <summary>
/// Clears the buffer without writing any buffered data to the output stream.
/// </summary>
public void Reset()
{
buffer.Clear();
maskPosition = 0;
}
/// <summary>
/// Writes the character either to the buffer or output stream and handles masking.
/// </summary>
private void WriteCharacter(char c)
{
if (mask[maskPosition] == c)
{
AddUnverifiedCharacter(c);
}
else
{
buffer.Add(c);
WriteVerifiedCharactersFromBuffer();
}
}
/// <summary>
/// Stores a character in the buffer that could be part of the mask.
/// </summary>
private void AddUnverifiedCharacter(char c)
{
buffer.Add(c);
maskPosition++;
if (maskPosition == mask.Length)
{
buffer.Clear();
maskPosition = 0;
foreach (var repChar in replacement)
{
output(repChar);
}
}
}
/// <summary>
/// Writes characters that cannot be part of the mask to the output.
/// </summary>
private void WriteVerifiedCharactersFromBuffer()
{
// Find possible new start position in buffer
var newStart = 0;
for (; newStart < buffer.Count; newStart++)
{
if (IsBufferPartOfMask(newStart))
{
WritePartOfBuffer(newStart);
buffer.RemoveRange(0, newStart);
maskPosition = buffer.Count;
return;
}
}
WritePartOfBuffer(buffer.Count);
buffer.Clear();
maskPosition = 0;
}
/// <summary>
/// Determines whether the buffer content can be part of the mask, starting at the given index.
/// </summary>
private bool IsBufferPartOfMask(int offset)
{
for (var i = 0; i < buffer.Count - offset; i++)
{
var c = buffer[offset + i];
if (c != mask[i])
{
return false;
}
}
return true;
}
/// <summary>
/// Writes the beginning part of buffer to the output
/// </summary>
private void WritePartOfBuffer(int count)
{
for (var i = 0; i < count; i++)
{
output(buffer[i]);
}
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I was looking for an A* search implementation in C#. Eventually wrote my own. Because it's universal, I hope it will be useful for others.
To use the algorithm, inherit a class from AStar<T> and override Neighbors, Cost and Heuristic functions to define your search domain. Type T defines a location in the search domain (for example Point when searching on a 2D grid). Then call FindPath on your class.
You may also optionally override StorageClear, StorageGet and StorageAdd to provide optimized per-location data storage for A*. The default implementation uses a Dictionary<T, object>, which is reasonably fast, but can be improved upon in many specific cases. If not overriding storage, make sure that type T is equatable (e.g. by implementing IEquatable<T>), otherwise dictionary lookups will be very slow.
See comments for more details.
public abstract class AStar<T>
{
#region Fields
private class Node
{
public T Position;
public T PreviousPosition;
public float F, G, H;
public bool IsClosed;
}
private int m_nodesCacheIndex;
private List<Node> m_nodesCache = new List<Node>();
private List<Node> m_openHeap = new List<Node>();
private List<T> m_neighbors = new List<T>();
private Dictionary<T, object> m_defaultStorage;
#endregion
#region Domain Definition
// User must override Neighbors, Cost and Heuristic functions to define search domain.
// It is optional to override StorageClear, StorageGet and StorageAdd functions.
// Default implementation of these storage functions uses a Dictionary<T, object>, this works for all possible search domains.
// A domain-specific storage algorihm may be significantly faster.
// For example if searching on a finite 2D or 3D grid, storage using fixed array with each element representing one world point benchmarks an order of magnitude faster.
/// <summary>
/// Return all neighbors of the given point.
/// Must be overridden.
/// </summary>
/// <param name="p">Point to return neighbors for</param>
/// <param name="neighbors">Empty collection to fill with neighbors</param>
protected abstract void Neighbors(T p, List<T> neighbors);
/// <summary>
/// Return cost of making a step from p1 to p2 (which are neighbors).
/// Cost equal to float.PositiveInfinity indicates that passage from p1 to p2 is impossible.
/// Must be overridden.
/// </summary>
/// <param name="p1">Start point</param>
/// <param name="p2">End point</param>
/// <returns>Cost value</returns>
protected abstract float Cost(T p1, T p2);
/// <summary>
/// Return an estimate of cost of moving from p to nearest goal.
/// Must return 0 when p is goal.
/// This is an estimate of sum of all costs along the best path between p and the nearest goal.
/// This should not overestimate the actual cost; if it does, the result of A* might not be an optimal path.
/// Underestimating the cost is allowed, but may cause the algorithm to check more positions.
/// Must be overridden.
/// </summary>
/// <param name="p">Point to estimate cost from</param>
/// <returns>Cost value</returns>
protected abstract float Heuristic(T p);
/// <summary>
/// Clear A* storage.
/// This will be called every time before a search starts and before any other user functions are called.
/// Optional override when using domain-optimized storage.
/// </summary>
protected virtual void StorageClear()
{
if (m_defaultStorage == null)
{
m_defaultStorage = new Dictionary<T, object>();
}
else
{
m_defaultStorage.Clear();
}
}
/// <summary>
/// Retrieve data from storage at given point.
/// Optional override when using domain-optimized storage.
/// </summary>
/// <param name="p">Point to retrieve data at</param>
/// <returns>Data stored for point p or null if nothing stored</returns>
protected virtual object StorageGet(T p)
{
object data;
m_defaultStorage.TryGetValue(p, out data);
return data;
}
/// <summary>
/// Add data to storage at given point.
/// There will never be any data already stored at that point.
/// Optional override when using domain-optimized storage.
/// </summary>
/// <param name="p">Point to add data at</param>
/// <param name="data">Data to add</param>
protected virtual void StorageAdd(T p, object data)
{
m_defaultStorage.Add(p, data);
}
#endregion
#region Public Interface
/// <summary>
/// Find best path from start to nearest goal.
/// Goal is any point for which Heuristic override returns 0.
/// If maxPositionsToCheck limit is reached, best path found so far is returned.
/// If there is no path to goal, path to a point nearest to goal is returned instead.
/// </summary>
/// <param name="path">Path will contain steps to reach goal from start in reverse order (first step at the end of collection)</param>
/// <param name="start">Starting point to search for path</param>
/// <param name="maxPositionsToCheck">Maximum number of positions to check</param>
/// <returns>True when path to goal was found, false if partial path only</returns>
public bool FindPath(ICollection<T> path, T start, int maxPositionsToCheck = int.MaxValue)
{
// Check arguments
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
// Reset cache and storage
path.Clear();
m_nodesCacheIndex = 0;
m_openHeap.Clear();
StorageClear();
// Put start node
Node startNode = NewNode(start, default(T), 0, 0);
StorageAdd(start, startNode);
HeapEnqueue(startNode);
// Astar loop
Node bestNode = null;
int checkedPositions = 0;
while (true)
{
// Get next node from heap
Node currentNode = m_openHeap.Count > 0 ? HeapDequeue() : null;
// Check end conditions
if (currentNode == null || checkedPositions >= maxPositionsToCheck)
{
// No more nodes or limit reached, path not found, return path to best node if possible
if (bestNode != null)
{
BuildPathFromEndNode(path, startNode, bestNode);
}
return false;
}
else if (Heuristic(currentNode.Position) <= 0)
{
// Node is goal, return path
BuildPathFromEndNode(path, startNode, currentNode);
return true;
}
// Remember node with best heuristic; ignore start node
if (currentNode != startNode && (bestNode == null || currentNode.H < bestNode.H))
{
bestNode = currentNode;
}
// Move current node from open to closed in the storage
currentNode.IsClosed = true;
++checkedPositions;
// Try all neighbors
m_neighbors.Clear();
Neighbors(currentNode.Position, m_neighbors);
for (int i = 0; i < m_neighbors.Count; ++i)
{
// Get position
T position = m_neighbors[i];
// Get existing node at position, if any
Node node = (Node)StorageGet(position);
// If position was already analyzed, ignore step
if (node != null && node.IsClosed == true)
{
continue;
}
// If position is not passable, ignore step
float cost = Cost(currentNode.Position, position);
if (cost == float.PositiveInfinity)
{
continue;
}
// Calculate A* values
float g = currentNode.G + cost;
float h = Heuristic(position);
// Update or create new node at position
if (node != null)
{
// Update existing node if better
if (g < node.G)
{
node.G = g;
node.F = g + node.H;
node.PreviousPosition = currentNode.Position;
HeapUpdate(node);
}
}
else
{
// Create new open node if not yet exists
node = NewNode(position, currentNode.Position, g, h);
StorageAdd(position, node);
HeapEnqueue(node);
}
}
}
}
#endregion
#region Internals
private void BuildPathFromEndNode(ICollection<T> path, Node startNode, Node endNode)
{
for (Node node = endNode; node != startNode; node = (Node)StorageGet(node.PreviousPosition))
{
path.Add(node.Position);
}
}
private void HeapEnqueue(Node node)
{
m_openHeap.Add(node);
HeapifyFromPosToStart(m_openHeap.Count - 1);
}
private Node HeapDequeue()
{
Node result = m_openHeap[0];
if (m_openHeap.Count <= 1)
{
m_openHeap.Clear();
}
else
{
m_openHeap[0] = m_openHeap[m_openHeap.Count - 1];
m_openHeap.RemoveAt(m_openHeap.Count - 1);
HeapifyFromPosToEnd(0);
}
return result;
}
private void HeapUpdate(Node node)
{
int pos = -1;
for (int i = 0; i < m_openHeap.Count; ++i)
{
if (m_openHeap[i] == node)
{
pos = i;
break;
}
}
HeapifyFromPosToStart(pos);
}
private void HeapifyFromPosToEnd(int pos)
{
while (true)
{
int smallest = pos;
int left = 2 * pos + 1;
int right = 2 * pos + 2;
if (left < m_openHeap.Count && m_openHeap[left].F < m_openHeap[smallest].F)
smallest = left;
if (right < m_openHeap.Count && m_openHeap[right].F < m_openHeap[smallest].F)
smallest = right;
if (smallest != pos)
{
Node tmp = m_openHeap[smallest];
m_openHeap[smallest] = m_openHeap[pos];
m_openHeap[pos] = tmp;
pos = smallest;
}
else
{
break;
}
}
}
private void HeapifyFromPosToStart(int pos)
{
int childPos = pos;
while (childPos > 0)
{
int parentPos = (childPos - 1) / 2;
Node parentNode = m_openHeap[parentPos];
Node childNode = m_openHeap[childPos];
if (parentNode.F > childNode.F)
{
m_openHeap[parentPos] = childNode;
m_openHeap[childPos] = parentNode;
childPos = parentPos;
}
else
{
break;
}
}
}
private Node NewNode(T position, T previousPosition, float g, float h)
{
while (m_nodesCacheIndex >= m_nodesCache.Count)
{
m_nodesCache.Add(new Node());
}
Node node = m_nodesCache[m_nodesCacheIndex++];
node.Position = position;
node.PreviousPosition = previousPosition;
node.F = g + h;
node.G = g;
node.H = h;
node.IsClosed = false;
return node;
}
#endregion
}
I am working on a system that needs to accept and display complex fractions. The code for accepting fractions and turning them in to a double works, but when I want to display that value, I need to convert back to a fractional representation.
EDIT: I have fixed the overflow problem, but that didnt solve fractions like 1/3 or 5/6. SO I have devised a very hacky way to do this. I have code which generates the decimal representation of every fraction 0->64 over 1->64, and saves the most simplified form. This way, I can iterate through the list and find the closest fraction, and simply display that. Will post code once I have some.
I have code now that works for the vast majority of numbers, but occasionally I will get a tiny fraction like 1/321. This gets converted to a double, but cannot be converted back, because in my approach, the numerator causes an integer overflow.
Here is my code, I'm wondering if there is a better approach, or if there is someway to safely convert these to longs without losing the precision needed for a correct result:
public static String DecimalToFraction(double dec)
{
string str = dec.ToString();
if (str.Contains('.'))
{
String[] parts = str.Split('.');
long whole = long.Parse(parts[0]);
long numerator = long.Parse(parts[1]);
long denominator = (long)Math.Pow(10, parts[1].Length);
long divisor = GCD(numerator, denominator);
long num = numerator / divisor;
long den = denominator / divisor;
String fraction = num + "/" + den;
if (whole > 0)
{
return whole + " " + fraction;
}
else
{
return fraction;
}
}
else
{
return str;
}
}
public static long GCD(long a, long b)
{
return b == 0 ? a : GCD(b, a % b);
}
Recently I had to code a similar scenario. In my case, converting from decimal to rational number had to be a little more mathematically correct so I ended up implementing a Continued Fraction algorithm.
Although it is tailored made to my concrete implementation of RationalNumber, you should get the idea. It's a relatively simple algorithm that works reasonably well for any rational number approximation. Note that the implementation will give you the closest approximation with the required precision.
/// <summary>
/// Represents a rational number with 64-bit signed integer numerator and denominator.
/// </summary>
[Serializable]
public struct RationalNumber : IComparable, IFormattable, IConvertible, IComparable<RationalNumber>, IEquatable<RationalNumber>
{
private const int MAXITERATIONCOUNT = 20;
public RationalNumber(long number) {...}
public RationalNumber(long numerator, long denominator) {...}
public RationalNumber(RationalNumber numerator, RationalNumer denominator) {...}
...
/// <summary>
/// Defines an implicit conversion of a 64-bit signed integer to a rational number.
/// </summary>
/// <param name="value">The value to convert to a rational number.</param>
/// <returns>A rational number that contains the value of the value parameter as its numerator and 1 as its denominator.</returns>
public static implicit operator RationalNumber(long value)
{
return new RationalNumber(value);
}
/// <summary>
/// Defines an explicit conversion of a rational number to a double-precision floating-point number.
/// </summary>
/// <param name="value">The value to convert to a double-precision floating-point number.</param>
/// <returns>A double-precision floating-point number that contains the resulting value of dividing the rational number's numerator by it's denominator.</returns>
public static explicit operator double(RationalNumber value)
{
return (double)value.numerator / value.Denominator;
}
...
/// <summary>
/// Adds two rational numbers.
/// </summary>
/// <param name="left">The first value to add.</param>
/// <param name="right">The second value to add.</param>
/// <returns>The sum of left and right.</returns>
public static RationalNumber operator +(RationalNumber left, RationalNumber right)
{
//First we try directly adding in a checked context. If an overflow occurs we use the least common multiple and return the result. If it overflows again, it
//will be up to the consumer to decide what he will do with it.
//Cost penalty should be minimal as adding numbers that cause an overflow should be very rare.
RationalNumber result;
try
{
long numerator = checked(left.numerator * right.Denominator + right.numerator * left.Denominator);
long denominator = checked(left.Denominator * right.Denominator);
result = new RationalNumber(numerator,denominator);
}
catch (OverflowException)
{
long lcm = RationalNumber.getLeastCommonMultiple(left.Denominator, right.Denominator);
result = new RationalNumber(left.numerator * (lcm / left.Denominator) + right.numerator * (lcm / right.Denominator), lcm);
}
return result;
}
private static long getGreatestCommonDivisor(long i1, long i2)
{
Debug.Assert(i1 != 0 || i2 != 0, "Whoops!. Both arguments are 0, this should not happen.");
//Division based algorithm
long i = Math.Abs(i1);
long j = Math.Abs(i2);
long t;
while (j != 0)
{
t = j;
j = i % j;
i = t;
}
return i;
}
private static long getLeastCommonMultiple(long i1, long i2)
{
if (i1 == 0 && i2 == 0)
return 0;
long lcm = i1 / getGreatestCommonDivisor(i1, i2) * i2;
return lcm < 0 ? -lcm : lcm;
}
...
/// <summary>
/// Returns the nearest rational number approximation to a double-precision floating-point number with a specified precision.
/// </summary>
/// <param name="target">Target value of the approximation.</param>
/// <param name="precision">Minimum precision of the approximation.</param>
/// <returns>Nearest rational number with, at least, the required precision.</returns>
/// <exception cref="System.ArgumentException">Can not find a rational number approximation with specified precision.</exception>
/// <exception cref="System.OverflowException">target is larger than Mathematics.RationalNumber.MaxValue or smaller than Mathematics.RationalNumber.MinValue.</exception>
/// <remarks>It is important to clarify that the method returns the first rational number found that complies with the specified precision.
/// The method is not required to return an exact rational number approximation even if such number exists.
/// The returned rational number will always be in coprime form.</remarks>
public static RationalNumber GetNearestRationalNumber(double target, double precision)
{
//Continued fraction algorithm: http://en.wikipedia.org/wiki/Continued_fraction
//Implemented recursively. Problem is figuring out when precision is met without unwinding each solution. Haven't figured out how to do that.
//Current implementation evaluates a Rational approximation for increasing algorithm depths until precision criteria is met or maximum depth is reached (MAXITERATIONCOUNT)
//Efficiency is probably improvable but this method will not be used in any performance critical code. No use in optimizing it unless there is a good reason.
//Current implementation works reasonably well.
RationalNumber nearestRational = RationalNumber.zero;
int steps = 0;
while (Math.Abs(target - (double)nearestRational) > precision)
{
if (steps > MAXITERATIONCOUNT)
throw new ArgumentException(Strings.RationalMaximumIterationsExceptionMessage, "precision");
nearestRational = getNearestRationalNumber(target, 0, steps++);
}
return nearestRational;
}
private static RationalNumber getNearestRationalNumber(double number, int currentStep, int maximumSteps)
{
long integerPart;
integerPart = checked((long)number);
double fractionalPart = number - integerPart;
while (currentStep < maximumSteps && fractionalPart != 0)
{
return integerPart + new RationalNumber(1, getNearestRationalNumber(1 / fractionalPart, ++currentStep, maximumSteps));
}
return new RationalNumber(integerPart);
}
}
UPDATE: Whoops, forgot to include the operator + code. Fixed it.
You could use BigRational, which Microsoft released under their BCL project on codeplex. It supports arbitrarily large rational numbers, and actually stores it internally as a ratio. The nice thing is that you can treat it largely as a normal numeric type, since all of the operators are overloaded for you.
Interestingly, it lacks a way to print the number as a decimal. I wrote some code that did this, though, in a previous answer of mine. However, there are no guarantees on its performance or quality (I barely remember writing it).
Keep the number as a fraction:
struct Fraction
{
private int _numerator;
private int _denominator;
public int Numerator { get { return _numerator; } }
public int Denominator { get { return _denominator; } }
public double Value { get { return ((double) Numerator)/Denominator; } }
public Fraction( int n, int d )
{
// move negative to numerator.
if( d < 0 )
{
_numerator = -n;
_denominator = -d;
}
else if( d > 0 )
{
_numerator = n;
_denominator = d;
}
else
throw new NumberFormatException( "Denominator cannot be 0" );
}
public void ToString()
{
string ret = "";
int whole = Numerator / Denominator;
if( whole != 0 )
ret += whole + " ";
ret += Math.Abs(Numerator % Denominator) + "/" + Denominator;
return ret;
}
}
Please check these 2 methods:
/// <summary>
/// Converts Decimals into Fractions.
/// </summary>
/// <param name="value">Decimal value</param>
/// <returns>Fraction in string type</returns>
public string DecimalToFraction(double value)
{
string result;
double numerator, realValue = value;
int num, den, decimals, length;
num = (int)value;
value = value - num;
value = Math.Round(value, 5);
length = value.ToString().Length;
decimals = length - 2;
numerator = value;
for (int i = 0; i < decimals; i++)
{
if (realValue < 1)
{
numerator = numerator * 10;
}
else
{
realValue = realValue * 10;
numerator = realValue;
}
}
den = length - 2;
string ten = "1";
for (int i = 0; i < den; i++)
{
ten = ten + "0";
}
den = int.Parse(ten);
num = (int)numerator;
result = SimplifiedFractions(num, den);
return result;
}
/// <summary>
/// Converts Fractions into Simplest form.
/// </summary>
/// <param name="num">Numerator</param>
/// <param name="den">Denominator</param>
/// <returns>Simplest Fractions in string type</returns>
string SimplifiedFractions(int num, int den)
{
int remNum, remDen, counter;
if (num > den)
{
counter = den;
}
else
{
counter = num;
}
for (int i = 2; i <= counter; i++)
{
remNum = num % i;
if (remNum == 0)
{
remDen = den % i;
if (remDen == 0)
{
num = num / i;
den = den / i;
i--;
}
}
}
return num.ToString() + "/" + den.ToString();
}
}
Google doesn't understand that "between" is the name of the function I'm looking for and returns nothing relevant.
Ex: I want to check if 5 is between 0 and 10 in only one operation
It isn't clear what you mean by "one operation", but no, there's no operator / framework method that I know of to determine if an item is within a range.
You could of course write an extension-method yourself. For example, here's one that assumes that the interval is closed on both end-points.
public static bool IsBetween<T>(this T item, T start, T end)
{
return Comparer<T>.Default.Compare(item, start) >= 0
&& Comparer<T>.Default.Compare(item, end) <= 0;
}
And then use it as:
bool b = 5.IsBetween(0, 10); // true
No, but you can write your own:
public static bool Between(this int num, int lower, int upper, bool inclusive = false)
{
return inclusive
? lower <= num && num <= upper
: lower < num && num < upper;
}
Here's a complete class.
/// <summary>
/// An extension class for the between operation
/// name pattern IsBetweenXX where X = I -> Inclusive, X = E -> Exclusive
///
/// </summary>
public static class BetweenExtensions
{
/// <summary>
/// Between check <![CDATA[min <= value <= max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Inclusive minimum border</param>
/// <param name="max">Inclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenII<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) <= 0) && (value.CompareTo(max) <= 0);
}
/// <summary>
/// Between check <![CDATA[min < value <= max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Exclusive minimum border</param>
/// <param name="max">Inclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenEI<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) < 0) && (value.CompareTo(max) <= 0);
}
/// <summary>
/// between check <![CDATA[min <= value < max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Inclusive minimum border</param>
/// <param name="max">Exclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenIE<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) <= 0) && (value.CompareTo(max) < 0);
}
/// <summary>
/// between check <![CDATA[min < value < max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Exclusive minimum border</param>
/// <param name="max">Exclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenEE<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) < 0) && (value.CompareTo(max) < 0);
}
}
plus some unit test code
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethodIsBeetween()
{
Assert.IsTrue(5.0.IsBetweenII(5.0, 5.0));
Assert.IsFalse(5.0.IsBetweenEI(5.0, 5.0));
Assert.IsFalse(5.0.IsBetweenIE(5.0, 5.0));
Assert.IsFalse(5.0.IsBetweenEE(5.0, 5.0));
Assert.IsTrue(5.0.IsBetweenII(4.9, 5.0));
Assert.IsTrue(5.0.IsBetweenEI(4.9, 5.0));
Assert.IsFalse(5.0.IsBetweenIE(4.9, 5.0));
Assert.IsFalse(5.0.IsBetweenEE(4.9, 5.0));
Assert.IsTrue(5.0.IsBetweenII(5.0, 5.1));
Assert.IsFalse(5.0.IsBetweenEI(5.0, 5.1));
Assert.IsTrue(5.0.IsBetweenIE(5.0, 5.1));
Assert.IsFalse(5.0.IsBetweenEE(5.0, 5.1));
Assert.IsTrue(5.0.IsBetweenII(4.9, 5.1));
Assert.IsTrue(5.0.IsBetweenEI(4.9, 5.1));
Assert.IsTrue(5.0.IsBetweenIE(4.9, 5.1));
Assert.IsTrue(5.0.IsBetweenEE(4.9, 5.1));
Assert.IsFalse(5.0.IsBetweenII(5.1, 4.9));
Assert.IsFalse(5.0.IsBetweenEI(5.1, 4.9));
Assert.IsFalse(5.0.IsBetweenIE(5.1, 4.9));
Assert.IsFalse(5.0.IsBetweenEE(5.1, 4.9));
}
}
Nope, you'll have to test each endpoint individually.
if ((x > 0) && (x < 10)) {
// do stuff
}
Or if you want it to look more "betweeny", reorder the args:
if ((0 < x) && (x < 10)) {
// do stuff
}
So far, it looks like none of the answers have considered the likely possibility that dynamically, you don't know which value is the lower and upper bound. For the general case, you could create your own IsBetween method that would probably go something like:
public bool IsBetween(double testValue, double bound1, double bound2)
{
return (testValue >= Math.Min(bound1,bound2) && testValue <= Math.Max(bound1,bound2));
}
As of c# 9, you can do 5 is > 0 and < 10; https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#pattern-matching-enhancements
There is no built in construct in C#/.NET, but you can easily add your own extension method for this:
public static class ExtensionsForInt32
{
public static bool IsBetween (this int val, int low, int high)
{
return val > low && val < high;
}
}
Which can be used like:
if (5.IsBetween (0, 10)) { /* Do something */ }
Except for the answer by #Ed G, all of the answers require knowing which bound is the lower and which is the upper one.
Here's a (rather non-obvious) way of doing it when you don't know which bound is which.
/// <summary>
/// Method to test if a value is "between" two other values, when the relative magnitude of
/// the two other values is not known, i.e., number1 may be larger or smaller than number2.
/// The range is considered to be inclusive of the lower value and exclusive of the upper
/// value, irrespective of which parameter (number1 or number2) is the lower or upper value.
/// This implies that if number1 equals number2 then the result is always false.
///
/// This was extracted from a larger function that tests if a point is in a polygon:
/// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
/// </summary>
/// <param name="testValue">value to be tested for being "between" the other two numbers</param>
/// <param name="number1">one end of the range</param>
/// <param name="number2">the other end of the range</param>
/// <returns>true if testValue >= lower of the two numbers and less than upper of the two numbers,
/// false otherwise, incl. if number1 == number2</returns>
private static bool IsInRange(T testValue, T number1, T number2)
{
return (testValue <= number1) != (testValue <= number2);
}
Note: This is NOT a generic method; it is pseudo code. The T in the above method should be replaced by a proper type, "int" or "float" or whatever. (There are ways of making this generic, but they're sufficiently messy that it's not worth while for a one-line method, at least not in most situations.)
Wouldn't it be as simple as
0 < 5 && 5 < 10
?
So I suppose if you want a function out of it you could simply add this to a utility class:
public static bool Between(int num, int min, int max) {
return min < num && num < max;
}
Generic function that is validated at compilation!
public static bool IsBetween<T>(this T item, T start, T end) where T : IComparable
{
return item.CompareTo(start) >= 0 && item.CompareTo(end) <= 0;
}
int val_to_check = 5
bool in_range = Enumerable.Range(0, 13).Contains(val_to_check);
The second parameter is the "count" not the end or high number.
I.E.
int low_num = 0
int high_num = 12
int val_to_check = 5
bool in_range = Enumerable.Range(low_num , high_num - low_num + 1).Contains(val_to_check);
Checks if the val_to_check is between 0 and 12
What about
somenumber == Math.Max(0,Math.Min(10,somenumber));
returns true when somenumber is 5.
returns false when somenumber is 11.
Refer to this link. A one line solution to that question.
How to elegantly check if a number is within a range?
int x = 30;
if (Enumerable.Range(1,100).Contains(x))
//true
if (x >= 1 && x <= 100)
//true
I know the post is pretty old, but It may help others..
I don't know that function; anyway if your value is unsigned, just one operation means (val < 11)... If it is signed, I think there is no atomic way to do it because 10 is not a power of 2...
As #Hellfrost pointed out, it is literally nonsense to compare a number to two different numbers in "one operation", and I know of no C# operator that encapsulates this.
between = (0 < 5 && 5 < 10);
Is about the most-compact form I can think of.
You could make a somewhat "fluent"-looking method using extension (though, while amusing, I think it's overkill):
public static bool Between(this int x, int a, int b)
{
return (a < x) && (x < b);
}
Use:
int x = 5;
bool b = x.Between(0,10);
Base #Dan J this version don't care max/min, more like sql :)
public static bool IsBetween(this decimal me, decimal a, decimal b, bool include = true)
{
var left = Math.Min(a, b);
var righ = Math.Max(a, b);
return include
? (me >= left && me <= righ)
: (me > left && me < righ)
;
}
Or if you want to bound the value to the interval:
public static T EnsureRange<T>(this T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0)
return min;
else if (value.CompareTo(max) > 0)
return max;
else
return value;
}
It is like two-sided Math.Min/Max
And for negatives values:
Public Function IsBetween(Of T)(item As T, pStart As T, pEnd As T) As Boolean ' https://msdn.microsoft.com/fr-fr/library/bb384936.aspx
Dim Deb As T = pStart
Dim Fin As T = pEnd
If (Comparer(Of T).Default.Compare(pStart, pEnd) > 0) Then Deb = pEnd : Fin = pStart
Return Comparer(Of T).Default.Compare(item, Deb) >= 0 AndAlso Comparer(Of T).Default.Compare(item, Fin) <= 0
End Function