I am a beginner in c# and .net.
My intention as a unix guy is to provide a service for the windows team collecting performance data from windows servers and recoding them in graphite (via StatsD).
I found a source code which seems to do exactly that except that it uses a wrong interface (wrong level of abstraction) it seems. e.g. the labels are translated in localized windows versions (which is insane) and that makes the use of this service infeasible.
Using wmi performance counters seems to be the way to go, but I would like to query efficiently and found that using a refresher object for this is the recommended solution however I do not know how to do that.
Somehow related I found an answer for querying the values once which I include here as a reference.
The question is:
How do I use a refresher object for querying disk statistics as seen on the previous link every 5 seconds?
(for bonus points) How do I integrate, what changes should I make to the service mentioned before to collect data via wmi instead of performance counters?
Thanks
Here is what I use for gathering information about disk usage via WMI in C#:
private List<DiskInfo> GetDiskInfo()
{
List<DiskInfo> disks = new List<DiskInfo>();
SelectQuery query = new SelectQuery("SELECT Size, FreeSpace, Name, FileSystem FROM Win32_LogicalDisk WHERE DriveType = 3");
ManagementObjectSearcher moSearcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection collection = moSearcher.Get();
foreach (ManagementObject res in collection)
{
float size = Convert.ToSingle(res["Size"]) / 1024f;
float usedSpace = size - (Convert.ToSingle(res["FreeSpace"]) / 1024f);
DiskInfo di = new DiskInfo();
di.Name = res["Name"].ToString();
di.Size = ConvertVal(size);
di.UsedSpace = ConvertVal(usedSpace);
if (size > 0)
{
di.PercentUsed = ((usedSpace / size) * 100).ToString("N0");
}
else
{
di.PercentUsed = "0";
}
if (res["FileSystem"] != null)
{
di.FileSystem = res["FileSystem"].ToString();
disks.Add(di);
}
}
return disks;
}
// handles returning the correct units
private string ConvertVal(float value)
{
float K = value;
float M = value / 1024f;
float G = M / 1024f;
float T = G / 1024f;
string unit = "KB";
float val = K;
if (K >= 1024)
{
unit = "MB";
val = M;
}
if (M >= 1024)
{
unit = "GB";
val = G;
}
if (G >= 1024)
{
unit = "TB";
val = T;
}
return val.ToString("N2") + unit;
}
I primarily use the code above in conjunction with a full ComputerInfo class that I can call from jQuery AJAX every few seconds on an ASP.NET MVC service that returns JSON to the browser and I construct the page on the fly with the data provided.
Here's my DiskInfo class for making data easier to display:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ComputerInfo.Models
{
public class DiskInfo
{
public string Name { get; set; }
public string Size { get; set; }
public string UsedSpace { get; set; }
public string PercentUsed { get; set; }
public string FileSystem { get; set; }
}
}
I hope this helps. Let me know if you need anything else in the answer.
Related
How can I get in c# the CPU frequency (example : 2Ghz) ?
It's simple but I don't find it in the environnement variables.
Thanks :)
var searcher = new ManagementObjectSearcher(
"select MaxClockSpeed from Win32_Processor");
foreach (var item in searcher.Get())
{
var clockSpeed = (uint)item["MaxClockSpeed"];
}
if you wish to get other fields look at class Win32_processor
Try this code
using System.Management;
uint currentsp , Maxsp;
public void CPUSpeed()
{
using(ManagementObject Mo = new ManagementObject("Win32_Processor.DeviceID='CPU0'"))
{
currentsp = (uint)(Mo["CurrentClockSpeed"]);
Maxsp = (uint)(Mo["MaxClockSpeed"]);
}
}
If you want to get the turbo speed, you can make use of the "% Processor Performance" performance counter and multiply it with the WMI "MaxClockSpeed" as follows:
private string GetCPUInfo()
{
PerformanceCounter cpuCounter = new PerformanceCounter("Processor Information", "% Processor Performance", "_Total");
double cpuValue = cpuCounter.NextValue();
Thread loop = new Thread(() => InfiniteLoop());
loop.Start();
Thread.Sleep(1000);
cpuValue = cpuCounter.NextValue();
loop.Abort();
foreach (ManagementObject obj in new ManagementObjectSearcher("SELECT *, Name FROM Win32_Processor").Get())
{
double maxSpeed = Convert.ToDouble(obj["MaxClockSpeed"]) / 1000;
double turboSpeed = maxSpeed * cpuValue / 100;
return string.Format("{0} Running at {1:0.00}Ghz, Turbo Speed: {2:0.00}Ghz", obj["Name"], maxSpeed, turboSpeed);
}
return string.Empty;
}
The InfiniteLoop method is simply an integer that gets 1 added and subtracted:
private void InfiniteLoop()
{
int i = 0;
while (true)
i = i + 1 - 1;
}
The InfiniteLoop method is just added to give the CPU something to do and turbo in the process. The loop is allowed to run for a second before the next value is taken and the loop aborted.
One could take the information out of the registry, but dunno if it works on Windows XP or older (mine is Windows 7).
HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/CentralProcessor/0/ProcessorName
reads like
Intel(R) Core(TM)2 Quad CPU Q6600 # 2.40GHz
for me.
Something like this code could retrieve the information (not tested):
RegistryKey processor_name = Registry.LocalMachine.OpenSubKey(#"Hardware\Description\System\CentralProcessor\0", RegistryKeyPermissionCheck.ReadSubTree);
if (processor_name != null)
{
if (processor_name.GetValue("ProcessorNameString") != null)
{
string value = processor_name.GetValue("ProcessorNameString");
string freq = value.Split('#')[1];
...
}
}
(source: here)
You can get it via WMI, but it's quite slow so if you're going to be getting it on more than one occasion I'd suggest you cache it - something like:
namespace Helpers
{
using System.Management;
public static class HardwareHelpers
{
private static uint? maxCpuSpeed = null;
public static uint MaxCpuSpeed
{
get
{
return maxCpuSpeed.HasValue ? maxCpuSpeed.Value : (maxCpuSpeed = GetMaxCpuSpeed()).Value;
}
}
private static uint GetMaxCpuSpeed()
{
using (var managementObject = new ManagementObject("Win32_Processor.DeviceID='CPU0'"))
{
var sp = (uint)(managementObject["MaxClockSpeed"]);
return sp;
}
}
}
}
I am looking to create an external application that monitors the 'FPS' of a DirectX application (like FRAPS without the recording). I have read several Microsoft articles on performance measuring tools - but I am looking to get the feedback (and experience) of the community.
My question: what is the best method for obtaining the FPS of a DirectX application?
Windows has some Event Tracing for Windows providers related to DirectX profiling. The most intresting ones are Microsoft-Windows-D3D9 and Microsoft-Windows-DXGI, which allow tracing of the frame presentation events. The simplest way to calculate FPS is to count the number of PresentStart events withing a time interval and divide that by the length of the interval.
To work with ETW in C#, install Microsoft.Diagnostics.Tracing.TraceEvent package.
The following code sample displays FPS of running processes:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using Microsoft.Diagnostics.Tracing.Session;
namespace ConsoleApp1
{
//helper class to store frame timestamps
public class TimestampCollection
{
const int MAXNUM = 1000;
public string Name { get; set; }
List<long> timestamps = new List<long>(MAXNUM + 1);
object sync = new object();
//add value to the collection
public void Add(long timestamp)
{
lock (sync)
{
timestamps.Add(timestamp);
if (timestamps.Count > MAXNUM) timestamps.RemoveAt(0);
}
}
//get the number of timestamps withing interval
public int QueryCount(long from, long to)
{
int c = 0;
lock (sync)
{
foreach (var ts in timestamps)
{
if (ts >= from && ts <= to) c++;
}
}
return c;
}
}
class Program
{
//event codes (https://github.com/GameTechDev/PresentMon/blob/40ee99f437bc1061a27a2fc16a8993ee8ce4ebb5/PresentData/PresentMonTraceConsumer.cpp)
public const int EventID_D3D9PresentStart = 1;
public const int EventID_DxgiPresentStart = 42;
//ETW provider codes
public static readonly Guid DXGI_provider = Guid.Parse("{CA11C036-0102-4A2D-A6AD-F03CFED5D3C9}");
public static readonly Guid D3D9_provider = Guid.Parse("{783ACA0A-790E-4D7F-8451-AA850511C6B9}");
static TraceEventSession m_EtwSession;
static Dictionary<int, TimestampCollection> frames = new Dictionary<int, TimestampCollection>();
static Stopwatch watch = null;
static object sync = new object();
static void EtwThreadProc()
{
//start tracing
m_EtwSession.Source.Process();
}
static void OutputThreadProc()
{
//console output loop
while (true)
{
long t1, t2;
long dt = 2000;
Console.Clear();
Console.WriteLine(DateTime.Now.ToString() + "." + DateTime.Now.Millisecond.ToString());
Console.WriteLine();
lock (sync)
{
t2 = watch.ElapsedMilliseconds;
t1 = t2 - dt;
foreach (var x in frames.Values)
{
Console.Write(x.Name + ": ");
//get the number of frames
int count = x.QueryCount(t1, t2);
//calculate FPS
Console.WriteLine("{0} FPS", (double)count / dt * 1000.0);
}
}
Console.WriteLine();
Console.WriteLine("Press any key to stop tracing...");
Thread.Sleep(1000);
}
}
public static void Main(string[] argv)
{
//create ETW session and register providers
m_EtwSession = new TraceEventSession("mysess");
m_EtwSession.StopOnDispose = true;
m_EtwSession.EnableProvider("Microsoft-Windows-D3D9");
m_EtwSession.EnableProvider("Microsoft-Windows-DXGI");
//handle event
m_EtwSession.Source.AllEvents += data =>
{
//filter out frame presentation events
if (((int)data.ID == EventID_D3D9PresentStart && data.ProviderGuid == D3D9_provider) ||
((int)data.ID == EventID_DxgiPresentStart && data.ProviderGuid == DXGI_provider))
{
int pid = data.ProcessID;
long t;
lock (sync)
{
t = watch.ElapsedMilliseconds;
//if process is not yet in Dictionary, add it
if (!frames.ContainsKey(pid))
{
frames[pid] = new TimestampCollection();
string name = "";
var proc = Process.GetProcessById(pid);
if (proc != null)
{
using (proc)
{
name = proc.ProcessName;
}
}
else name = pid.ToString();
frames[pid].Name = name;
}
//store frame timestamp in collection
frames[pid].Add(t);
}
}
};
watch = new Stopwatch();
watch.Start();
Thread thETW = new Thread(EtwThreadProc);
thETW.IsBackground = true;
thETW.Start();
Thread thOutput = new Thread(OutputThreadProc);
thOutput.IsBackground = true;
thOutput.Start();
Console.ReadKey();
m_EtwSession.Dispose();
}
}
}
Based on the source code of PresentMon project.
Fraps inserts a DLL into every running application and hooks specific DX calls to figure out the framerate and capture video, pretty sure that you'll have to do something similar. After a bit of poking around I found a Github project that does some basic DX hooking for doing captures and overlays, so that might be a good spot to start out with. Though I've not used it personally so I can't totally vouch for the quality.
http://spazzarama.com/2011/03/14/c-screen-capture-and-overlays-for-direct3d-9-10-and-11-using-api-hooks/
Building on https://stackoverflow.com/a/54625953/12047161:
I had more success not using the stopwatch as the event triggers seems to be asynchronous with the actual frames. I kept getting batches of 20-50 frames all at once, making the estimated FPS fluctuate between 50 and 250% of the actual value.
Instead i used TimeStampRelativeMSec
//handle event
m_EtwSession.Source.AllEvents += data =>
{
//filter out frame presentation events
if((int) data.ID == EventID_DxgiPresentStart && data.ProviderGuid == DXGI_provider)
{
int pid = data.ProcessID;
long t;
t = watch.ElapsedMilliseconds;
//if process is not yet in Dictionary, add it
if (!frames.ContainsKey(pid))
{
frames[pid] = new TimestampCollection();
string name = "";
var proc = Process.GetProcessById(pid);
if (proc != null)
{
using (proc)
{
name = proc.ProcessName;
}
}
else name = pid.ToString();
frames[pid].Name = name;
}
frames[pid].Add((long)data.TimeStampRelativeMSec);
}
};
property from the TraceEvent class, and calculate FPS by rounding the average time between an arbitrary number of past entries:
public double GetFrameTime(int count)
{
double returnValue = 0;
int listCount = timestamps.Count;
if(listCount > count)
{
for(int i = 1; i <= count; i++)
{
returnValue += timestamps[listCount - i] - timestamps[listCount - (i + 1)];
}
returnValue /= count;
}
return returnValue;
}
This method gave me far more accurate (Compared to, as available, in-game counters) of several different games i've tried.
Twice this month, I've had to create a total a bunch of records of a class. (two different classes) and this will happen again. It seems to me that there should be an easy way to do this using reflection for any class without having to code a totaling routine for each class.
Consider:
private class ThisAndThat
{
public int This { get; set; }
public float That { get; set; }
public double TheOther { get; set; }
public string Whatever { get; set; }
}
As my code rumbles along, I create a bunch of these but I also need a totaling routine. Something like the AddToTotal() listed below,m where the numbers are added and the string is ignored.
List<ThisAndThat> _Discovered = new List<ThisAndThat>();
ThisAndThat _Total = new List<ThisAndThat>;
while( !Finished )
{
ThisAndThat CurrentOne = GetAnotherOne();
_Discovered.Add( CurrentOne );
AddToTotal( _Total, CurrentOne );
}
Obviously the numeric three properties in this sample class are easy to code, but I just did one with 60 numeric members. I fumbled around with reflection for a while but could not come up with a routine.
Reflection can absolutely do this. It's not too difficult. Here's an example using the class you provided:
var tat = new ThisAndThat();
tat.This = 1;
tat.That = 2.0F;
tat.TheOther = 3.0;
tat.Whatever = "Whatever";
var type = typeof(ThisAndThat);
var properties = type.GetProperties();
double total = 0.0;
foreach (System.Reflection.PropertyInfo pi in properties)
{
switch (pi.PropertyType.ToString())
{
case "System.Int32": //int
total += (int) pi.GetValue(tat, null);
break;
case "System.Double":
total += (double) pi.GetValue(tat, null);
break;
case "System.Single": //float
total += (float) pi.GetValue(tat, null);
break;
}
}
MessageBox.Show(total.ToString());
Note that my sample only works with Properties. If you have Fields that you need totaled, you'll have to use the GetFields method on the Type.
You should also be aware of handling other numeric types as well such as Int64, etc...
Is this what you're looking for?
ThisAndThat thisThatSum = 0;
foreach(ThisAndThat tat in _Discovered)
{
thisThatSum.This += tat.This;
// do the same for other fields
}
I'm sure a linq, way exists too, but I'd have to like do research and stuff to get that to you
Linq to sql makes this very easy for anything IEnumerable
ThisAndThat item1 = new ThisAndThat();
ThisAndThat item2 = new ThisAndThat();
item1.TheOther = 1.00;
item2.TheOther = 2.00;
_Discovered.Add(item1);
_Discovered.Add(item2);
var amount = from p in _Discovered
select p.TheOther;
Console.WriteLine("Amount total is {0}", amount.Sum());
How can I get in c# the CPU frequency (example : 2Ghz) ?
It's simple but I don't find it in the environnement variables.
Thanks :)
var searcher = new ManagementObjectSearcher(
"select MaxClockSpeed from Win32_Processor");
foreach (var item in searcher.Get())
{
var clockSpeed = (uint)item["MaxClockSpeed"];
}
if you wish to get other fields look at class Win32_processor
Try this code
using System.Management;
uint currentsp , Maxsp;
public void CPUSpeed()
{
using(ManagementObject Mo = new ManagementObject("Win32_Processor.DeviceID='CPU0'"))
{
currentsp = (uint)(Mo["CurrentClockSpeed"]);
Maxsp = (uint)(Mo["MaxClockSpeed"]);
}
}
If you want to get the turbo speed, you can make use of the "% Processor Performance" performance counter and multiply it with the WMI "MaxClockSpeed" as follows:
private string GetCPUInfo()
{
PerformanceCounter cpuCounter = new PerformanceCounter("Processor Information", "% Processor Performance", "_Total");
double cpuValue = cpuCounter.NextValue();
Thread loop = new Thread(() => InfiniteLoop());
loop.Start();
Thread.Sleep(1000);
cpuValue = cpuCounter.NextValue();
loop.Abort();
foreach (ManagementObject obj in new ManagementObjectSearcher("SELECT *, Name FROM Win32_Processor").Get())
{
double maxSpeed = Convert.ToDouble(obj["MaxClockSpeed"]) / 1000;
double turboSpeed = maxSpeed * cpuValue / 100;
return string.Format("{0} Running at {1:0.00}Ghz, Turbo Speed: {2:0.00}Ghz", obj["Name"], maxSpeed, turboSpeed);
}
return string.Empty;
}
The InfiniteLoop method is simply an integer that gets 1 added and subtracted:
private void InfiniteLoop()
{
int i = 0;
while (true)
i = i + 1 - 1;
}
The InfiniteLoop method is just added to give the CPU something to do and turbo in the process. The loop is allowed to run for a second before the next value is taken and the loop aborted.
One could take the information out of the registry, but dunno if it works on Windows XP or older (mine is Windows 7).
HKEY_LOCAL_MACHINE/HARDWARE/DESCRIPTION/CentralProcessor/0/ProcessorName
reads like
Intel(R) Core(TM)2 Quad CPU Q6600 # 2.40GHz
for me.
Something like this code could retrieve the information (not tested):
RegistryKey processor_name = Registry.LocalMachine.OpenSubKey(#"Hardware\Description\System\CentralProcessor\0", RegistryKeyPermissionCheck.ReadSubTree);
if (processor_name != null)
{
if (processor_name.GetValue("ProcessorNameString") != null)
{
string value = processor_name.GetValue("ProcessorNameString");
string freq = value.Split('#')[1];
...
}
}
(source: here)
You can get it via WMI, but it's quite slow so if you're going to be getting it on more than one occasion I'd suggest you cache it - something like:
namespace Helpers
{
using System.Management;
public static class HardwareHelpers
{
private static uint? maxCpuSpeed = null;
public static uint MaxCpuSpeed
{
get
{
return maxCpuSpeed.HasValue ? maxCpuSpeed.Value : (maxCpuSpeed = GetMaxCpuSpeed()).Value;
}
}
private static uint GetMaxCpuSpeed()
{
using (var managementObject = new ManagementObject("Win32_Processor.DeviceID='CPU0'"))
{
var sp = (uint)(managementObject["MaxClockSpeed"]);
return sp;
}
}
}
}
class FxRate {
string Base { get; set; }
string Target { get; set; }
double Rate { get; set; }
}
private IList<FxRate> rates = new List<FxRate> {
new FxRate {Base = "EUR", Target = "USD", Rate = 1.3668},
new FxRate {Base = "GBP", Target = "USD", Rate = 1.5039},
new FxRate {Base = "USD", Target = "CHF", Rate = 1.0694},
new FxRate {Base = "CHF", Target = "SEK", Rate = 8.12}
// ...
};
Given a large yet incomplete list of exchange rates where all currencies appear at least once (either as a target or base currency): What algorithm would I use to be able to derive rates for exchanges that aren't directly listed?
I'm looking for a general purpose algorithm of the form:
public double Rate(string baseCode, string targetCode, double currency)
{
return ...
}
In the example above a derived rate would be GBP->CHF or EUR->SEK (which would require using the conversions for EUR->USD, USD->CHF, CHF->SEK)
Whilst I know how to do the conversions by hand I'm looking for a tidy way (perhaps using LINQ) to perform these derived conversions perhaps involving multiple currency hops, what's the nicest way to go about this?
First construct a graph of all your currencies:
private Dictionary<string, List<string>> _graph
public void ConstructGraph()
{
if (_graph == null) {
_graph = new Dictionary<string, List<string>>();
foreach (var rate in rates) {
if (!_graph.ContainsKey(rate.Base))
_graph[rate.Base] = new List<string>();
if (!_graph.ContainsKey(rate.Target))
_graph[rate.Target] = new List<string>();
_graph[rate.Base].Add(rate.Target);
_graph[rate.Target].Add(rate.Base);
}
}
}
Now traverse that graph using recursion:
public double Rate(string baseCode, string targetCode)
{
if (_graph[baseCode].Contains(targetCode)) {
// found the target code
return GetKnownRate(baseCode, targetCode);
}
else {
foreach (var code in _graph[baseCode]) {
// determine if code can be converted to targetCode
double rate = Rate(code, targetCode);
if (rate != 0) // if it can than combine with returned rate
return rate * GetKnownRate(baseCode, code);
}
}
return 0; // baseCode cannot be converted to the targetCode
}
public double GetKnownRate(string baseCode, string targetCode)
{
var rate = rates.SingleOrDefault(fr => fr.Base == baseCode && fr.Target == targetCode);
var rate_i rates.SingleOrDefault(fr => fr.Base == targetCode && fr.Target == baseCode));
if (rate == null)
return 1 / rate_i.Rate
return rate.Rate;
}
Disclaimer: This is untested. Further, I'm sure this isn't the most performant approach to solve the problem (O(n) I think), but I believe it will work. There are a number of things you could add to improve the performance (e.g. saving every new combined rate calculation would eventually turn this into an effective O(1))
Wouldn't it be simpler to just have a list of all conversions to a single currency and then use that for any conversion? So something like (with USD as the base currency):
var conversionsToUSD = new Dictionary<string, decimal>();
public decimal Rate ( string baseCode, string targetCode )
{
if ( targetCode == "USD" )
return conversionsToUSD[baseCode];
if ( baseCode == "USD" )
return 1 / conversionsToUSD[targetCode];
return conversionsToUSD[baseCode] / conversionsToUSD[targetCode]
}
Now, this assumes that algebra is perfectly communicative. I.e., if I convert to EUR->USD->GBP I'll get the same as converting from EUR->GBP. That might not actually be the case in reality in which case, you would need every supported permutation.
Interesting problem!
First off, stay clear from double / floating point arithmetic. The .NET Decimal type should be quite sufficient and provide better precision! Such improved precision may be particularly important given the fact that the calculation of derived Fx rates requires a chain of multiple operations.
Another remark is that it is probably off-limits to introduce a simpler/shorter list of Exchange rates, whereby the Target is always the same [real or fictitious] currency. I'm assuming here that we should use the listed rate when available.
So figuring out derived rates should become a [simplified] network solution, whereby
Given a Base and Target currencies, we identify all the shortest pathes (from Base to Target), given the authoritative (non derived) rates in the list. (We can hope that the shortest path would be 2, in all cases, but this may not be the case given very esoteric currencies).
for each of these shortest paths (I think it would be ludicrous to also consider longer pathes), we perform the simple arithmetic conversion, and...
hopefully confirm that these derived rates are all within a nominal margin of conversion error and therefore take the average of these rates
raise some alert... or just make a lot of money by using making a circular path and raking in the differential ;-)
I have no idea what that "double currency" is for... i'll just ignore it.
Attempt: List<List<FxRate>> res = Rates("EUR", "CHF"); yields {EUR-USD, USD-CHF}.
Looks promising! :)
public class FxRate
{
public string Base { get; set; }
public string Target { get; set; }
public double Rate { get; set; }
}
private List<FxRate> rates = new List<FxRate>
{
new FxRate {Base = "EUR", Target = "USD", Rate = 1.3668},
new FxRate {Base = "GBP", Target = "USD", Rate = 1.5039},
new FxRate {Base = "USD", Target = "CHF", Rate = 1.0694},
new FxRate {Base = "CHF", Target = "SEK", Rate = 8.12}
// ...
};
public List<List<FxRate>> Rates(string baseCode, string targetCode)
{
return Rates(baseCode, targetCode, rates.ToArray());
}
public List<List<FxRate>> Rates(string baseCode, string targetCode, FxRate[] toSee)
{
List<List<FxRate>> results = new List<List<FxRate>>();
List<FxRate> possible = toSee.Where(r => r.Base == baseCode).ToList();
List<FxRate> hits = possible.Where(p => p.Target == targetCode).ToList();
if (hits.Count > 0)
{
possible.RemoveAll(hits.Contains);
results.AddRange(hits.Select(hit => new List<FxRate> { hit }));
}
FxRate[] newToSee = toSee.Where( item => !possible.Contains(item)).ToArray();
foreach (FxRate posRate in possible)
{
List<List<FxRate>> otherConversions = Rates(posRate.Target, targetCode, newToSee);
FxRate rate = posRate;
otherConversions.ForEach(result => result.Insert(0, rate));
results.AddRange(otherConversions);
}
return results;
}
Comments?
PS: you can get the cheaper convertion with double minConvertion = res.Min(r => r.Sum(convertion => convertion.Rate));
The most straight-forward algorithm would probably just be like Dijkstra's shortest path or something on a graph you generate using that list. Being that you don't know beforehand how long the path will be, this isn't really a problem that can be elegantly solved by a LINQ query. (Not that it's not possible, it's just probably not what you should pursue.)
On the other hand, if you know that there is a path from any currency to any other, and that there is only one possible conversion between any two currencies on the list (ie, if USD > EUR and USD > CHF exist, then EUR > CHF doesn't exist or you can ignore it), you can simply generate something like a doubly linked list and traverse. Again though, this isn't something that can be elegantly solved through LINQ.
Generate all of them and cache them. Given initial set this function will generate all existing pairs (inside same list) without graphs or recursion, by simple expanding initial list as it iterates.
public static void CrossRates(List<FxRate> rates)
{
for (int i = 0; i < rates.Count; i++)
{
FxRate rate = rates[i];
for (int j = i + 1; j < rates.Count; j++)
{
FxRate rate2 = rates[j];
FxRate cross = CanCross(rate, rate2);
if (cross != null)
if (rates.FirstOrDefault(r => r.Ccy1.Equals(cross.Ccy1) && r.Ccy2.Equals(cross.Ccy2)) == null)
rates.Add(cross);
}
}
}
This utility function will generate individual cross rate.
public static FxRate CanCross(FxRate r1, FxRate r2)
{
FxRate nr = null;
if (r1.Ccy1.Equals(r2.Ccy1) && r1.Ccy2.Equals(r2.Ccy2) ||
r1.Ccy1.Equals(r2.Ccy2) && r1.Ccy2.Equals(r2.Ccy1)
) return null; // Same with same.
if (r1.Ccy1.Equals(r2.Ccy1))
{ // a/b / a/c = c/b
nr = new FxRate()
{
Ccy1 = r2.Ccy2,
Ccy2 = r1.Ccy2,
Rate = r1.Rate / r2.Rate
};
}
else if (r1.Ccy1.Equals(r2.Ccy2))
{
// a/b * c/a = c/b
nr = new FxRate()
{
Ccy1 = r2.Ccy1,
Ccy2 = r1.Ccy2,
Rate = r2.Rate * r1.Rate
};
}
else if (r1.Ccy2.Equals(r2.Ccy2))
{
// a/c / b/c = a/b
nr = new FxRate()
{
Ccy1 = r1.Ccy1,
Ccy2 = r2.Ccy1,
Rate = r1.Rate / r2.Rate
};
}
else if (r1.Ccy2.Equals(r2.Ccy1))
{
// a/c * c/b = a/b
nr = new FxRate()
{
Ccy1 = r1.Ccy1,
Ccy2 = r2.Ccy2,
Rate = r1.Rate * r2.Rate
};
}
return nr;
}