Convert double number to digits and vice versa? - c#

I'm trying to convert a double number into array of digits
Input:
double num
Output:
int[] arrDigit
int dotIdx
bool isMinus
for example:
Input:
double num = -69.69777
Output:
int[] arrDigit = { 7,7,7,9,6,9,6}
int dotIdx = 5
bool isMinus = true
And vice versa:
Input:
array of input digit commands
Output:
double num
for example:
Input:
Insert digit 6
Insert digit 9
Start dot
Insert digit 6
Insert digit 9
Insert digit 7
Insert digit 7
Insert digit 7
Output:
double num=69.69777
The easiest way is using C# string method, I've implemented it:
class DigitToNumTranslator
{
private bool m_isDot;
//Minus is handled as operator, not the job for translator
//Helper
private StringBuilder m_builder = new StringBuilder();
public double NumResult
{
get
{
return double.Parse(m_builder.ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
}
public void Reset()
{
m_builder.Clear();
m_isDot = false;
}
public void StartDot()
{
if (!m_isDot)
{
m_isDot = true;
m_builder.Append('.');
}
}
public void InsertDigit(int digit)
{
m_builder.Append(digit.ToString());
}
}
class NumToDigitTranslator
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslator()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_lstDigit.Clear();
m_dotIdx = 0;
m_isMinus = false;
var szNum = num.ToString(System.Globalization.CultureInfo.InvariantCulture);
//Won't work if it's 1E+17
for (var i = 0; i < szNum.Length; ++i)
{
if (char.IsNumber(szNum[i]))
m_lstDigit.Add(int.Parse(szNum[i].ToString()));
else if (szNum[i] == '-')
m_isMinus = true;
else if (szNum[i] == '.')
m_dotIdx = i;
}
//Reverse for display
if (m_dotIdx != 0)
m_dotIdx = szNum.Length - 1 - m_dotIdx;
m_lstDigit.Reverse();
}
}
But the string method is met with the issue "1E+17" (when the number is too long). I don't like the string method very much because it may have unexpected bug (e.g CultureInfo, 1E+17,... ) who knows if there is more case that I don't know - too risky and my application doesn't use string to display number, it combines sprite image to draw the number.
So I'd like to try the math method:
class DigitToNumTranslatorRaw
{
private double m_numResult;
private bool m_isDot;
private int m_dotIdx;
public double NumResult => m_numResult;
public void Reset()
{
m_numResult = 0;
m_dotIdx = 1;
m_isDot = false;
}
public void StartDot()
{
m_isDot = true;
}
public void InsertDigit(int digit)
{
if (m_isDot)
{
m_numResult += digit * Math.Pow(10, -m_dotIdx);
++m_dotIdx;
}
else
{
m_numResult *= 10;
m_numResult += digit;
}
}
}
class NumToDigitTranslatorRaw
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public NumToDigitTranslatorRaw()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_dotIdx = 0;
m_lstDigit.Clear();
//WIP (work with int, but not with double, thus failed to get the numbers after dot)
var intNum = (int)num;
while (num > 10)
{
m_lstDigit.Add((intNum % 10));
num /= 10;
}
if (m_lstDigit.Count > 0)
m_lstDigit.Reverse();
else
m_lstDigit.Add(0);
}
}
But I meet with 2 problems:
In DigitToNumTranslatorRaw, I don't now if it's better than the string solution. the m_numResult += digit * Math.Pow(10, -m_dotIdx);, num /= 10;,... may cause floating point precision problem and Is Pow the best way for performance?
In NumToDigitTranslatorRaw, I'm still not able to get the number after dot.
I tried to extract the code TryParse of Mircosoft to see how they do it, but it's too complicated I couldn't find where they put the that code.
So my purpose is:
Math method: write DigitToNumTranslatorRaw & NumToDigitTranslatorRaw and make sure it's bug free & floating point accurate & better performance than string method (because I don't deal with CultureInfo.InvariantCulture, 1E+17,...).
If the math method is too hard, I'll just use the string method DigitToNumTranslator & NumToDigitTranslator and deal with each string problem (e.g too long number turn into 1E+17), but the problem is I don't know if I cover all the string problem (e.g the 1E+17 I found out by random testing, the CultureInfo problem I found out by searching on stack overflow), the docs didn't list all the problems I may encounter.

Code usage example:
Digit to number:
private DigitToNumTranslator m_digit = new DigitToNumTranslator();
m_digit.Reset();
var isEnd = false;
//m_lstInputKey is a list of enum E_INPUT_KEY, created earlier by user input
for (; i < m_lstInputKey.Count; ++i)
{
switch (m_lstInputKey[i])
{
case E_INPUT_KEY.NUM_0: m_digit.InsertDigit(0); break;
case E_INPUT_KEY.NUM_1: m_digit.InsertDigit(1); break;
case E_INPUT_KEY.NUM_2: m_digit.InsertDigit(2); break;
case E_INPUT_KEY.NUM_3: m_digit.InsertDigit(3); break;
case E_INPUT_KEY.NUM_4: m_digit.InsertDigit(4); break;
case E_INPUT_KEY.NUM_5: m_digit.InsertDigit(5); break;
case E_INPUT_KEY.NUM_6: m_digit.InsertDigit(6); break;
case E_INPUT_KEY.NUM_7: m_digit.InsertDigit(7); break;
case E_INPUT_KEY.NUM_8: m_digit.InsertDigit(8); break;
case E_INPUT_KEY.NUM_9: m_digit.InsertDigit(9); break;
case E_INPUT_KEY.NUM_DOT: m_digit.StartDot(); break;
default: isEnd = true; break;
}
if (isEnd) break;
}
Console.WriteLine(m_digit.NumResult);
Number to digit:
private NumToDigitTranslator m_numToDigitTranslator = new NumToDigitTranslator();
double dInputNumber = 6969696969696969696996.69696969696969D;
m_numToDigitTranslator.Translate(dInputNumber);
//Draw function is how you draw the information to the screen
DrawListDigit(m_numToDigitTranslator.LstDigit);
DrawMinus(m_numToDigitTranslator.IsMinus);
DrawDot(m_numToDigitTranslator.DotIdx);
Math solution
Code:
#region MATH_WAY
class DigitToNumTranslatorMath
{
private double m_numResult;
private bool m_isDot;
private int m_dotIdx;
public double NumResult => m_numResult;
public void Reset()
{
m_numResult = 0;
m_dotIdx = 1;
m_isDot = false;
}
public void StartDot()
{
m_isDot = true;
}
public void InsertDigit(int digit)
{
if (m_isDot)
{
m_numResult += digit * Math.Pow(10, -m_dotIdx);
++m_dotIdx;
}
else
{
m_numResult *= 10;
m_numResult += digit;
}
}
}
//Bug: (num - Math.Truncate(num))
//==> floating point problem
//==> 1.9D - Math.Truncate(1.9D) = 0.89999999999999991 (Expected: 0.9)
class NumToDigitTranslatorMath
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslatorMath()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_dotIdx = 0;
m_lstDigit.Clear();
m_isMinus = num < 0;
int intDigit;
double intNum;//Use double type to prevent casting a too big double for int which causes overflow
//Get the digits on the right of dot
const int NUM_COUNT_AFTER_DOT = 1000000000;//double has Precision 15-16 digits, but I only need 9 digits
//Math.Truncate(-1.9)=>-1; Math.Floor(-1.9)=>-2;
intNum = Math.Truncate((num - Math.Truncate(num)) * NUM_COUNT_AFTER_DOT);//Floating point bug here!!!
//Remove zeros
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
if (intDigit != 0)
break;
else
intNum = Math.Truncate(intNum / 10);
}
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
intNum = Math.Truncate(intNum / 10);
m_lstDigit.Add(intDigit);
++m_dotIdx;
}
//Get the digits on the left of dot
intNum = Math.Truncate(num);
while (intNum > 0)
{
intDigit = (int)(intNum % 10);
intNum = Math.Truncate(intNum / 10);
m_lstDigit.Add(intDigit);
}
if (m_lstDigit.Count == 0)
m_lstDigit.Add(0);
}
}
#endregion
Note: There is the floating point problem, for example: 1.9D - Math.Truncate(1.9D) = 0.89999999999999991 (Expected: 0.9).
I was planning to extract the code from .Net source code to implement it the Math way, but I was too lazy so I'll just use the String solution.
String Solution:
Code:
static class CONST_STR_FORMAT
{
private static System.Globalization.CultureInfo s_ciCommon = System.Globalization.CultureInfo.InvariantCulture;
public static System.Globalization.CultureInfo CI_COMMON => s_ciCommon;
//source: https://stackoverflow.com/questions/1546113/double-to-string-conversion-without-scientific-notation
public const string FORMAT_DOUBLE = "0.###################################################################################################################################################################################################################################################################################################################################################";
}
class DigitToNumTranslator
{
private bool m_isDot;
//Minus is handled as operator, not the job for translator
//Helper
private StringBuilder m_builder = new StringBuilder();
public double NumResult
{
get
{
return double.Parse(m_builder.ToString(), CONST_STR_FORMAT.CI_COMMON);
}
}
public void Reset()
{
m_builder.Clear();
m_isDot = false;
}
public void StartDot()
{
if (!m_isDot)
{
m_isDot = true;
m_builder.Append('.');
}
}
public void InsertDigit(int digit)
{
m_builder.Append(digit);
}
}
class NumToDigitTranslator
{
private List<int> m_lstDigit;
private IList<int> m_lstDigitReadOnly;
private int m_dotIdx;
private bool m_isMinus;
public IList<int> LstDigit => m_lstDigitReadOnly;
public int DotIdx => m_dotIdx;
public bool IsMinus => m_isMinus;
public NumToDigitTranslator()
{
m_lstDigit = new List<int>();
m_lstDigitReadOnly = m_lstDigit.AsReadOnly();
}
public void Translate(double num)
{
m_lstDigit.Clear();
m_dotIdx = 0;
m_isMinus = false;
var szNum = num.ToString(CONST_STR_FORMAT.FORMAT_DOUBLE, CONST_STR_FORMAT.CI_COMMON);
for (var i = 0; i < szNum.Length; ++i)
{
if (char.IsNumber(szNum[i]))
m_lstDigit.Add(int.Parse(szNum[i].ToString()));
else if (szNum[i] == '-')
m_isMinus = true;
else if (szNum[i] == '.')
m_dotIdx = i;
}
//Reverse for display
if (m_dotIdx != 0)
m_dotIdx = szNum.Length - 1 - m_dotIdx;
m_lstDigit.Reverse();
}
}
Note: No more headache. What I'm afraid most is bugs by culture (bug that happens on some devices but not on my device), hope the code System.Globalization.CultureInfo.InvariantCulture will make sure that nightmare won't happen.

Related

How to limit EditText length to 6 integers and 3 decimal places in C#(MonoDroid)?

Hello, I'm new here, don't be so hard, I'm creating a project in Xamarin.Andriod using c #.
I am trying to do a validation of an EditText in Xamarin.Android (C # / MonoDroid).
The condition that I must fulfill is as follows:
'It allows entering 6 integers and 3 decimal places, in a range from 0 to 999999.999'
I have found some possible solutions in java (I don't know much and I have not been able to implement them in C #) and they are the following:
TextWatcher with Java
InputFilter with Java
I don't know how to apply it in C #, if someone could explain or give me a simple example but in C #.
this is my EditText:
```<EditText
android:id="#+id/etVolume"
android:layout_width="#dimen/value_zero"
android:hint="Volumen"
android:textColor="#color/grisPrimario"
android:layout_height="fill_parent"
android:layout_weight="5"
android:textSize="#dimen/fluidListItem_text_size"
android:inputType="numberDecimal"
android:maxLength ="9"
android:textColorHint="#android:color/darker_gray"
android:backgroundTint="#color/grisPrimario"
android:gravity="bottom"
android:layout_marginBottom="-3dp"
android:layout_marginRight="#dimen/fluidListItemMarginRight"/>```
and my fragment is defined this EditText like this:
Volume = itemAdministeredFluid.FindViewById<EditText>(Resource.Id.etVolume);
etVolume.setFilters(new Android.Text.IInputFilter[] {new DecimalDigitsInputFilter(6,3)});
but i don't understand how to implement the method.
I saw that with java they use "TextWatcher" and "InputFilter" but I don't understand how to change it to C#
I appreciate any help I've spent days on this.
Thank you very much for the help.
It allows entering 6 integers and 3 decimal places, in a range from 0 to 999999.999'
I create IInputFilter that you can take a look:
public class DecimalDigitsInputFilter : Java.Lang.Object, IInputFilter
{
private double _min = 0;
private double _max = 0;
public DecimalDigitsInputFilter(double min, double max)
{
_min = min;
_max = max;
}
public ICharSequence FilterFormatted(ICharSequence source, int start, int end, ISpanned dest, int dstart, int dend)
{
try
{
string val = dest.ToString().Insert(dstart, source.ToString());
if (val.Contains("."))
{
string[] array = val.Split(".");
string value1 = array[0].ToString();
string value2 = array[1].ToString();
if (value1.Length > 6|| value1=="")
{
}
else
{
if(value2!="" && value2.Length<=3)
{
double input = double.Parse(val);
if (IsInRange(_min, _max, input))
return null;
}
else if(value2=="")
{
if(IsInRange(_min,_max,double.Parse(value1)))
{
return null;
}
}
}
}
else
{
if(val.Length<=6)
{
double input = double.Parse(val);
if (IsInRange(_min, _max, input))
return null;
}
}
}
catch (System.Exception ex)
{
Console.WriteLine("FilterFormatted Error: " + ex.Message);
}
return new Java.Lang.String(string.Empty);
}
private bool IsInRange(double min, double max, double input)
{
return max > min ? input >= min && input <= max : input >= max && input <= min;
}
}
edittext1 = FindViewById<EditText>(Resource.Id.editText1);
edittext1.SetFilters(new Android.Text.IInputFilter[] {new DecimalDigitsInputFilter(0,999999.999) });
It is better to do that in code, add a function to check inside TextWatcher. Since this will be called whenever the user types something.
In that function, you can check your condition and display an error message if the condition does not satisfy.
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// call checkNumber() function here
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
In C#
public class TextWatcher : Java.Lang.Object, ITextWatcher
{
public void AfterTextChanged(IEditable s)
{
//
}
public void BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
//
}
public void OnTextChanged(ICharSequence s, int start, int before, int count)
{
//
}
}
Add the listener to your EditText
editText.AddTextChangedListener(new TextWatcher());
It solves it as follows using InputFilter:
In the Fragment:
public EditText Volume;
Volume = itemAdministeredFluid.FindViewById<EditText>(Resource.Id.etVolume);
Volume.SetFilters(new IInputFilter[] { new DecimalDigitsInputFilter(6, 3) });
public class DecimalDigitsInputFilter : Java.Lang.Object, IInputFilter
{
string regexStr = string.Empty;
public DecimalDigitsInputFilter(int digitsBeforeZero, int digitsAfterZero)
{
regexStr = "^[0-9]{0," + (digitsBeforeZero) + "}([.][0-9]{0," + (digitsAfterZero - 1) + "})?$";
}
public Java.Lang.ICharSequence FilterFormatted(Java.Lang.ICharSequence source, int start, int end, ISpanned dest, int dstart, int dend)
{
Regex regex = new Regex(regexStr);
if (regex.IsMatch(dest.ToString()) || dest.ToString().Equals(""))
{
if (dest.ToString().Length < 6 && source.ToString() != ".")
{
return new Java.Lang.String(source.ToString());
}
else if (source.ToString() == ".")
{
return new Java.Lang.String(source.ToString());
}
else if (dest.ToString().Length >= 7 && dest.ToString().Length <= 10)
{
return new Java.Lang.String(source.ToString());
}
else
{
return new Java.Lang.String(string.Empty);
}
}
return new Java.Lang.String(string.Empty);
}
}
In the AXML
<EditText
android:id="#+id/etVolume"
android:layout_width="#dimen/value_zero"
android:hint="Volumen"
android:textColor="#color/grisPrimario"
android:layout_height="fill_parent"
android:layout_weight="5"
android:textSize="#dimen/fluidListItem_text_size"
android:inputType="numberDecimal"
android:maxLength ="10"
android:textColorHint="#android:color/darker_gray"
android:backgroundTint="#color/grisPrimario"
android:gravity="bottom"
android:layout_marginBottom="-3dp"
android:layout_marginRight="#dimen/fluidListItemMarginRight"/>
This response can be improved and the filters removed by modifying and building the ReGex better.
it works completely. does not allow entering more values after entering the 6 decimal places, the condition only allows entering the point (.) and then the 3 decimal places.
I think it can be cleaner if you create a better RegEx.
revising I am left with a condition that to change the integers the entire value must be deleted.
If you have a cleaner answer I hope to see it to implement it.

How to optimize recursive function for a graph for clearing and settlement

I have to make a module in an Insurance application that deals with clearing and settlement (I think this is the correct financial terminology) between insurance companies enroled in the system. Practically, the system must pair all the amounts that companies have to pay to one another, and only the unpaired (remaining) sums to be paid through the bank. For now there are about 30 companies in the system.
All the readings I did about clearing and settlement pointed me towards graphs and graphs theory (which I have studied in the highschool quite a long time ago).
For a system with 4 companies the graph would look like this:
where each company represents a node (N1 ... N4) and each weighted edge represents the amount that a company has to pay to the other. In my code, the nodes are int, representing the id's of the companies.
What I did so far... I created the graph (for test I used the Random generator for the amounts) and made a recursive function to calculate all posible cycles in the graph. Then I made another recursive function that takes all non-zero cycles starting with the longest path with maximum common sum to pair.
The algorithm seems valid in terms of final results, but for graphs bigger than 7-8 nodes it takes too long to complete. The problem is in the recursive function that creates the possible cycles in the graph. Here is my code:
static void Main(string[] args)
{
int nodes = 4;
try
{
nodes = Convert.ToInt32(args[0]);
}
catch { }
DateTime start = DateTime.Now;
Graph g = new Graph(nodes);
int step = 0;
double CompensatedAmount = 0;
double TotalCompensatedAmount = 0;
DateTime endGeneration = DateTime.Now;
Console.WriteLine("Graph generated in: " + (endGeneration - start).TotalSeconds + " seconds.");
Compensare.RunCompensation(false, g, step, CompensatedAmount, TotalCompensatedAmount, out CompensatedAmount, out TotalCompensatedAmount);
DateTime endCompensation = DateTime.Now;
Console.WriteLine("Graph compensated in: " + (endCompensation - endGeneration).TotalSeconds + " seconds.");
}
... and the main class:
public static class Compensare
{
public static void RunCompensation(bool exit, Graph g, int step, double prevCompensatedAmount, double prevTotalCompensatedAmount, out double CompensatedAmount, out double TotalCompensatedAmount)
{
step++;
CompensatedAmount = prevCompensatedAmount;
TotalCompensatedAmount = prevTotalCompensatedAmount;
if (!exit)
{
List<Cycle> orderedList = g.Cycles.OrderByDescending(x => x.CycleCompensatedAmount).ToList();
g.ListCycles(orderedList, "OrderedCycles" + step.ToString() + ".txt");
using (Graph clona = g.Clone())
{
int maxCycleIndex = clona.GetMaxCycleByCompensatedAmount();
double tmpCompensatedAmount = clona.Cycles[maxCycleIndex].CycleMin;
exit = tmpCompensatedAmount <= 0 ? true : false;
CompensatedAmount += tmpCompensatedAmount;
TotalCompensatedAmount += (tmpCompensatedAmount * clona.Cycles[maxCycleIndex].EdgesCount);
clona.CompensateCycle(maxCycleIndex);
clona.UpdateCycles();
Console.WriteLine(String.Format("{0} - edges: {4} - min: {3} - {1} - {2}\r\n", step, CompensatedAmount, TotalCompensatedAmount, tmpCompensatedAmount, clona.Cycles[maxCycleIndex].EdgesCount));
RunCompensation(exit, clona, step, CompensatedAmount, TotalCompensatedAmount, out CompensatedAmount, out TotalCompensatedAmount);
}
}
}
}
public class Edge
{
public int Start { get; set; }
public int End { get; set; }
public double Weight { get; set; }
public double InitialWeight {get;set;}
public Edge() { }
public Edge(int _start, int _end, double _weight)
{
this.Start = _start;
this.End = _end;
this.Weight = _weight;
this.InitialWeight = _weight;
}
}
public class Cycle
{
public List<Edge> Edges = new List<Edge>();
public double CycleWeight = 0;
public double CycleMin = 0;
public double CycleMax = 0;
public double CycleAverage = 0;
public double CycleCompensatedAmount = 0;
public int EdgesCount = 0;
public Cycle() { }
public Cycle(List<Edge> _edges)
{
this.Edges = new List<Edge>(_edges);
UpdateCycle();
}
public void UpdateCycle()
{
UpdateCycle(this);
}
public void UpdateCycle(Cycle c)
{
double sum = 0;
double min = c.Edges[0].Weight;
double max = c.Edges[0].Weight;
for(int i=0;i<c.Edges.Count;i++)
{
sum += c.Edges[i].Weight;
min = c.Edges[i].Weight < min ? c.Edges[i].Weight : min;
max = c.Edges[i].Weight > max ? c.Edges[i].Weight : max;
}
c.EdgesCount = c.Edges.Count;
c.CycleWeight = sum;
c.CycleMin = min;
c.CycleMax = max;
c.CycleAverage = sum / c.EdgesCount;
c.CycleCompensatedAmount = min * c.EdgesCount;
}
}
public class Graph : IDisposable
{
public List<int> Nodes = new List<int>();
public List<Edge> Edges = new List<Edge>();
public List<Cycle> Cycles = new List<Cycle>();
public int NodesCount { get; set; }
public Graph() { }
public Graph(int _nodes)
{
this.NodesCount = _nodes;
GenerateNodes();
GenerateEdges();
GenerateCycles();
}
private int FindNode(string _node)
{
for(int i = 0; i < this.Nodes.Count; i++)
{
if (this.Nodes[i].ToString() == _node)
return i;
}
return 0;
}
private int FindEdge(string[] _edge)
{
for(int i = 0; i < this.Edges.Count; i++)
{
if (this.Edges[i].Start.ToString() == _edge[0] && this.Edges[i].End.ToString() == _edge[1] && Convert.ToDouble(this.Edges[i].Weight) == Convert.ToDouble(_edge[2]))
return i;
}
return 0;
}
public Graph Clone()
{
Graph clona = new Graph();
clona.Nodes = new List<int>(this.Nodes);
clona.Edges = new List<Edge>(this.Edges);
clona.Cycles = new List<Cycle>(this.Cycles);
clona.NodesCount = this.NodesCount;
return clona;
}
public void CompensateCycle(int cycleIndex)
{
for(int i = 0; i < this.Cycles[cycleIndex].Edges.Count; i++)
{
this.Cycles[cycleIndex].Edges[i].Weight -= this.Cycles[cycleIndex].CycleMin;
}
}
public int GetMaxCycleByCompensatedAmount()
{
int toReturn = 0;
for (int i = 0; i < this.Cycles.Count; i++)
{
if (this.Cycles[i].CycleCompensatedAmount > this.Cycles[toReturn].CycleCompensatedAmount)
{
toReturn = i;
}
}
return toReturn;
}
public void GenerateNodes()
{
for (int i = 0; i < this.NodesCount; i++)
{
this.Nodes.Add(i + 1);
}
}
public void GenerateEdges()
{
Random r = new Random();
for(int i = 0; i < this.Nodes.Count; i++)
{
for(int j = 0; j < this.Nodes.Count; j++)
{
if(this.Nodes[i] != this.Nodes[j])
{
int _weight = r.Next(0, 500);
Edge e = new Edge(this.Nodes[i], this.Nodes[j], _weight);
this.Edges.Add(e);
}
}
}
}
public void GenerateCycles()
{
for(int i = 0; i < this.Edges.Count; i++)
{
FindCycles(new Cycle(new List<Edge>() { this.Edges[i] }));
}
this.UpdateCycles();
}
public void UpdateCycles()
{
for (int i = 0; i < this.Cycles.Count; i++)
{
this.Cycles[i].UpdateCycle();
}
}
private void FindCycles(Cycle path)
{
List<Edge> nextPossibleEdges = GetNextEdges(path.Edges[path.Edges.Count - 1].End);
for (int i = 0; i < nextPossibleEdges.Count; i++)
{
if (path.Edges.IndexOf(nextPossibleEdges[i]) < 0) // the edge shouldn't be already in the path
{
Cycle temporaryPath = new Cycle(path.Edges);
temporaryPath.Edges.Add(nextPossibleEdges[i]);
if (nextPossibleEdges[i].End == temporaryPath.Edges[0].Start) // end of path - valid cycle
{
if (!CycleExists(temporaryPath))
{
this.Cycles.Add(temporaryPath);
break;
}
}
else
{
FindCycles(temporaryPath);
}
}
}
}
private bool CycleExists(Cycle cycle)
{
bool toReturn = false;
if (this.Cycles.IndexOf(cycle) > -1) { toReturn = true; }
else
{
for (int i = 0; i < this.Cycles.Count; i++)
{
if (this.Cycles[i].Edges.Count == cycle.Edges.Count && !CompareEdges(this.Cycles[i].Edges[0], cycle.Edges[0]))
{
bool cycleExists = true;
for (int j = 0; j < cycle.Edges.Count; j++)
{
bool edgeExists = false; // if there is an edge not in the path, then the searched cycle is diferent from the current cycle and we can pas to the next iteration
for (int k = 0; k < this.Cycles[i].Edges.Count; k++)
{
if (CompareEdges(cycle.Edges[j], this.Cycles[i].Edges[k]))
{
edgeExists = true;
break;
}
}
if (!edgeExists)
{
cycleExists = false;
break;
}
}
if (cycleExists) // if we found an cycle with all edges equal to the searched cycle, then the cycle is not valid
{
toReturn = true;
break;
}
}
}
}
return toReturn;
}
private bool CompareEdges(Edge e1, Edge e2)
{
return (e1.Start == e2.Start && e1.End == e2.End && e1.Weight == e2.Weight);
}
private List<Edge> GetNextEdges(int endNode)
{
List<Edge> tmp = new List<Edge>();
for(int i = 0; i < this.Edges.Count; i++)
{
if(endNode == this.Edges[i].Start)
{
tmp.Add(this.Edges[i]);
}
}
return tmp;
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
this.Nodes = null;
this.Edges = null;
this.Cycles = null;
this.NodesCount = 0;
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~Graph() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
}
I've found several articles/answers about graphs, both in Java and C# (including quickgraph), but they mainly focus on directed graphs (without cycles).
I have also read about tail call optimization, for recursion, but I don't know if/how to implement in my case.
I now there is a lot to grasp about this subject, but maybe someone had to deal with something similar and can either help me optimize the code (which as I said seems to do the job in the end), either point me to another direction to rethink the whole process.
I think you can massively simplify this.
All money is the same, so (using your example) N1 doesn't care whether it gets 350 from N2 and pays 150 to N2 and so on - N1 merely cares that overall it ends up 145 down (if I've done the arithmetic correctly). Similarly, each other N only cares about its overall position. So, summing the inflows and outflows at each node, we get:
Company Net position
N1 -145
N2 -65
N3 +195
N4 +15
So with someone to act as a central clearing house - the bank - simply arrange for N1 and N2 to pay the clearing house 145 and 65 respectively, and for N3 and N4 to receive 195 and 15 respectively from the clearing house. And everyone's happy.
I may have missed some aspect, of course, in which case I'm sure someone will point it out...

Postfix increment into if, c#

Code example:
using System;
public class Test {
public static void Main() {
int a = 0;
if(a++ == 0){
Console.WriteLine(a);
}
}
}
In this code the Console will write: 1. I can write this code in another way:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
These two examples work exactly the same (from what I know about postfix).
The problem is with this example coming from the Microsoft tutorials:
using System;
public class Document {
// Class allowing to view the document as an array of words:
public class WordCollection {
readonly Document document;
internal WordCollection (Document d){
document = d;
}
// Helper function -- search character array "text", starting
// at character "begin", for word number "wordCount". Returns
//false if there are less than wordCount words. Sets "start" and
//length to the position and length of the word within text
private bool GetWord(char[] text, int begin, int wordCount,
out int start, out int length) {
int end = text.Length;
int count = 0;
int inWord = -1;
start = length = 0;
for (int i = begin; i <= end; ++i){
bool isLetter = i < end && Char.IsLetterOrDigit(text[i]);
if (inWord >= 0) {
if (!isLetter) {
if (count++ == wordCount) {//PROBLEM IS HERE!!!!!!!!!!!!
start = inWord;
length = i - inWord;
return true;
}
inWord = -1;
}
} else {
if (isLetter) {
inWord = i;
}
}
}
return false;
}
//Indexer to get and set words of the containing document:
public string this[int index] {
get
{
int start, length;
if(GetWord(document.TextArray, 0, index, out start,
out length)) {
return new string(document.TextArray, start, length);
} else {
throw new IndexOutOfRangeException();
}
}
set {
int start, length;
if(GetWord(document.TextArray, 0, index, out start,
out length))
{
//Replace the word at start/length with
// the string "value"
if(length == value.Length){
Array.Copy(value.ToCharArray(), 0,
document.TextArray, start, length);
}
else {
char[] newText = new char[document.TextArray.Length +
value.Length - length];
Array.Copy(document.TextArray, 0, newText, 0, start);
Array.Copy(value.ToCharArray(), 0, newText, start, value.Length);
Array.Copy(document.TextArray, start + length, newText,
start + value.Length, document.TextArray.Length - start - length);
document.TextArray = newText;
}
} else {
throw new IndexOutOfRangeException();
}
}
}
public int Count {
get {
int count = 0, start = 0, length = 0;
while (GetWord(document.TextArray, start + length,
0, out start, out length)) {
++count;
}
return count;
}
}
}
// Class allowing the document to be viewed like an array
// of character
public class CharacterCollection {
readonly Document document;
internal CharacterCollection(Document d) {
document = d;
}
//Indexer to get and set character in the containing
//document
public char this[int index] {
get {
return document.TextArray[index];
}
set {
document.TextArray[index] = value;
}
}
//get the count of character in the containing document
public int Count {
get {
return document.TextArray.Length;
}
}
}
//Because the types of the fields have indexers,
//these fields appear as "indexed properties":
public WordCollection Words;
public readonly CharacterCollection Characters;
private char[] TextArray;
public Document(string initialText) {
TextArray = initialText.ToCharArray();
Words = new WordCollection(this);
Characters = new CharacterCollection(this);
}
public string Text {
get {
return new string(TextArray);
}
}
class Test {
static void Main() {
Document d = new Document(
"peter piper picked a peck of pickled peppers. How many pickled peppers did peter piper pick?"
);
//Change word "peter" to "penelope"
for(int i = 0; i < d.Words.Count; ++i){
if (d.Words[i] == "peter") {
d.Words[i] = "penelope";
}
}
for (int i = 0; i < d.Characters.Count; ++i) {
if (d.Characters[i] == 'p') {
d.Characters[i] = 'P';
}
}
Console.WriteLine(d.Text);
}
}
}
If I change the code marked above to this:
if (count == wordCount) {//PROBLEM IS HERE
start = inWord;
length = i - inWord;
count++;
return true;
}
I get an IndexOutOfRangeException, but I don't know why.
Your initial assumption is incorrect (that the two examples work exactly the same). In the following version, count is incremented regardless of whether or not it is equal to wordCount:
if (count++ == wordCount)
{
// Code omitted
}
In this version, count is ONLY incremented when it is equal to wordCount
if (count == wordCount)
{
// Other code omitted
count++;
}
EDIT
The reason this is causing you a failure is that, when you are searching for the second word (when wordCount is 1), the variable count will never equal wordCount (because it never gets incremented), and therefore the GetWord method returns false, which then triggers the else clause in your get method, which throws an IndexOutOfRangeException.
In your version of the code, count is only being incremented when count == wordCount; in the Microsoft version, it's being incremented whether the condition is met or not.
using System;
public class Test {
public static void Main() {
int a = 0;
if(a++ == 0){
Console.WriteLine(a);
}
}
}
Is not quite the same as:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
In the second case a++ is executed only if a == 0. In the first case a++ is executed every time we check the condition.
There is your mistake:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
}
It should be like this:
public static void Main() {
int a = 0;
if(a == 0){
a++;
Console.WriteLine(a);
}
else
a++;
}
a gets alwasy increased. This means, that in your code example count will get only increased when count == wordCount (In which case the method will return true anyway...). You basicly never increasing count.

C# : Console.Read() does not get the "right" input

I have the following code:
The actual problem is the "non-quoted" code.
I want to get the player amount (max = 4), but when I ask via Console.Read() and I enter any Int from 1 to 4 I get as value: 48 + Console.Read().
They only thing how I can get the "real" input is using Console.ReadLine(), but this does not give me an Integer, no it returns a string, and actually do not know how to convert String (Numbers) to Integers in C#, because I am new, and because I only found ToString() and not ToNumber.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace eve_calc_tool
{
class Program
{
int players;
int units;
int active_units;
int inactive_units;
int finished_units;
int lastDiceNumber = 0;
bool game_state;
public static void Main(string[] args)
{
int count_game = 0;
//Console.Title = "Mensch ärger dich nicht";
//Console.WriteLine("\tNeues Spiel wird");
//Console.WriteLine("\t...geladen");
//System.Threading.Thread.Sleep(5000);
//Console.Clear();
//Console.WriteLine("Neues Spiel wird gestartet, bitte haben sie etwas Geduld");
//Console.Title = "Spiel " + count_game.ToString();
//Console.Clear();
//string prevText = "Anzahl der Spieler: ";
//Console.WriteLine(prevText);
string read = Console.ReadLine();
/*Program game = new Program();
game.players = read;
game.setPlayers(game.players);
if (game.players > 0 && 5 > game.players)
{
game.firstRound();
}*/
string readagain = read;
Console.ReadLine();
}
/*
bool setPlayers(int amount)
{
players = amount;
if (players > 0)
{
return true;
}
else
{
return false;
}
}
bool createGame()
{
inactive_units = units = getPlayers() * 4;
active_units = 0;
finished_units = 0;
game_state = true;
if (game_state == true)
{
return true;
}
else
{
return false;
}
}
int getPlayers()
{
return players;
}
private static readonly Random random = new Random();
private static readonly object syncLock = new object();
public static int RandomNumber(int min, int max)
{
lock (syncLock)
{ // synchronize
return random.Next(min, max);
}
}
int rollDice()
{
lastDiceNumber = RandomNumber(1,6);
return lastDiceNumber;
}
int firstRound()
{
int[] results = new int[getPlayers()];
for (int i = 0; i < getPlayers(); i++)
{
results[i] = rollDice();
}
Array.Sort(results);
return results[3];
}
*/
}
}
You can use
int convertedNumber = int.parse(stringToConvert)
or
int convertedNumber;
int.TryParse(stringToConvert, out covertedNumber)
to convert strings to integers.
You should really use TryParse instead so that you can catch if the user doesn't input a number. int.Parse will throw an exception if it tries to convert a string that is not numeric.
int convertedNumber = 0;
if (!int.TryParse(stringToConvert, out convertedNumber))
{
// this code will execute if the user did not put
// in an actual number. For example, if the user entered "a".
}
The TryParse method returns a boolean value which will tell you whether the conversion was successful. If it was successful, the converted value will be passed through the out parameter.
To convert your string to an integer, use int.Parse(yourString).
The reason you get "48 + Console.ReadKey" is that Console.ReadKey returns the code of the key that was pressed - in this case, the ANSI value of the number character that was pressed.

Auto Generate alphanumeric Unique Id with C#

Total string length is 5 chars
I have a scenario, ID starts with
A0001 and ends with A9999 then
B0001 to B9999 until F0001 to f9999
after that
FA001 to FA999 then
FB001 to FB999 until ....FFFF9
Please suggest any idea on how to create this format.
public static IEnumerable<string> Numbers()
{
return Enumerable.Range(0xA0000, 0xFFFF9 - 0xA0000 + 1)
.Select(x => x.ToString("X"));
}
You could also have an id generator class:
public class IdGenerator
{
private const int Min = 0xA0000;
private const int Max = 0xFFFF9;
private int _value = Min - 1;
public string NextId()
{
if (_value < Max)
{
_value++;
}
else
{
_value = Min;
}
return _value.ToString("X");
}
}
I am a few years late. But I hope my answer will help everyone looking for a good ID Generator. None of the previous answers work as expected and do not answer this question.
My answer fits the requirements perfectly. And more!!!
Notice that setting the _fixedLength to ZERO will create dynamically sized ID's.
Setting it to anything else will create FIXED LENGTH ID's;
Notice also that calling the overload that takes a current ID will "seed" the class and consecutive calls DO NOT need to be called with another ID. Unless you had random ID's and need the next one on each.
Enjoy!
public static class IDGenerator
{
private static readonly char _minChar = 'A';
private static readonly char _maxChar = 'C';
private static readonly int _minDigit = 1;
private static readonly int _maxDigit = 5;
private static int _fixedLength = 5;//zero means variable length
private static int _currentDigit = 1;
private static string _currentBase = "A";
public static string NextID()
{
if(_currentBase[_currentBase.Length - 1] <= _maxChar)
{
if(_currentDigit <= _maxDigit)
{
var result = string.Empty;
if(_fixedLength > 0)
{
var prefixZeroCount = _fixedLength - _currentBase.Length;
if(prefixZeroCount < _currentDigit.ToString().Length)
throw new InvalidOperationException("The maximum length possible has been exeeded.");
result = result = _currentBase + _currentDigit.ToString("D" + prefixZeroCount.ToString());
}
else
{
result = _currentBase + _currentDigit.ToString();
}
_currentDigit++;
return result;
}
else
{
_currentDigit = _minDigit;
if(_currentBase[_currentBase.Length - 1] == _maxChar)
{
_currentBase = _currentBase.Remove(_currentBase.Length - 1) + _minChar;
_currentBase += _minChar.ToString();
}
else
{
var newChar = _currentBase[_currentBase.Length - 1];
newChar++;
_currentBase = _currentBase.Remove(_currentBase.Length - 1) + newChar.ToString();
}
return NextID();
}
}
else
{
_currentDigit = _minDigit;
_currentBase += _minChar.ToString();
return NextID();
}
}
public static string NextID(string currentId)
{
if(string.IsNullOrWhiteSpace(currentId))
return NextID();
var charCount = currentId.Length;
var indexFound = -1;
for(int i = 0; i < charCount; i++)
{
if(!char.IsNumber(currentId[i]))
continue;
indexFound = i;
break;
}
if(indexFound > -1)
{
_currentBase = currentId.Substring(0, indexFound);
_currentDigit = int.Parse(currentId.Substring(indexFound)) + 1;
}
return NextID();
}
}
This is a sample of the ouput using _fixedLength = 4 and _maxDigit = 5
A001
A002
A003
A004
A005
B001
B002
B003
B004
B005
C001
C002
C003
C004
C005
AA01
AA02
AA03
AA04
AA05
AB01
AB02
AB03
AB04
AB05
AC01
AC02
AC03
AC04
AC05
see this code
private void button1_Click(object sender, EventArgs e)
{
string get = label1.Text.Substring(7); //label1.text=ATHCUS-100
MessageBox.Show(get);
string ou="ATHCUS-"+(Convert.ToInt32(get)+1).ToString();
label1.Text = ou.ToString();
}
Run this query in order to get the last ID in the database
SELECT TOP 1 [ID_COLUMN] FROM [NAME_OF_TABLE] ORDER BY [ID_COLUMN] DESC
Read the result to a variable and then run the following function on the result in order to get the next ID.
public string NextID(string lastID)
{
var allLetters = new string[] {"A", "B", "C", "D", "E", "F"};
var lastLetter = lastID.Substring(0, 1);
var lastNumber = int.Parse(lastID.Substring(1));
if (Array.IndexOf(allLetters, lastLetter) < allLetters.Length - 1 &&
lastNumber == 9999)
{
//increase the letter
lastLetter = allLetters(Array.IndexOf(allLetters, lastLetter) + 1);
lastNumber = 0;
} else {
lastLetter = "!";
}
var result = lastLetter + (lastNumber + 1).ToString("0000");
//ensure we haven't exceeded the upper limit
if (result.SubString(0, 1) == "!") {
result = "Upper Bounds Exceeded!";
}
return result;
}
DISCLAIMER
This code will only generate the first set of IDs. I do not understand the process of generating the second set.
If you need to take it from the database and do this you can use something like the following.
int dbid = /* get id from db */
string id = dbid.ToString("X5");
This should give you the format you are looking for as a direct convert from the DB ID.

Categories