Binomial tree calibrated to yield curve and volatility structure in C#

In case anyone finds it useful, here it is. I employed backward induction, which means $O(N^2)$ calibration time, and my solver is disgustingly primitive, but it is simple and it works. And it uses $O(N)$ memory.
Usecase is:

```        public void Calculate() {
var tree = new BinomialTree(
new[] {0.0608, 0.0611, 0.0621, 0.0631, 0.0655},
new[] {0.00, 0.17, 0.16, 0.15, 0.13});
// now you can use tree.TreeRate(i,j) function to find rates in tree nodes
}
```

And the code is:

```public class Solver {
public double? Solve(Func<double, double> what, double from, double to) {
Debug.Assert(Math.Sign(what(from))!= Math.Sign(what(to)), "Function must be of different signs on borders");
do {
var mid = (to + from)/2;
var valOnTo = what(to);
var valOnMid = what(mid);
if (Math.Sign(valOnTo) != Math.Sign(valOnMid))
from = mid;
else to = mid;
} while (Math.Abs(from-to) > 10e-5);
return (to + from) / 2;
}
}

public class BinomialTree {

/// <summary>
/// Calculates interest rate in (i, j) node in tree
/// </summary>
/// <param name="i">number of time step</param>
/// <param name="j">number of point by vertical axis, j = 0 is the highest, j = i is the lowest</param>
/// <returns>Double value - an interest rate in (i, j) node in tree</returns>
public double TreeRate(int i, int j) {
Debug.Assert(i < _n && j < _n && j <= i, "Requested node is out of the tree");
return _treeRates[i]*Math.Exp(-2*_treeVols[i]*j);
}

public BinomialTree(double[] rates, double[] vols, double[] terms = null) {
Debug.Assert(rates.Length == vols.Length, "Count of rates and volatilities must be equal");

_n = rates.Length;
_treeRates = new double[_n];
_treeVols = vols;
if (terms != null) {
Debug.Assert(terms.Length == vols.Length, "Count of rates and volatilities must be equal");
_treeTerms = terms;
} else {
_treeTerms = new double[_n];
for (var i = 0; i < _n; i++) _treeTerms[i] = i+1;
}
_treeRates[0] = rates[0];

var solver = new Solver();
for (var i = 1; i < _n; i++) {
// 1) calculating price of ZCB with term i and rate r[i]
var price = Math.Exp(-rates[i]*_treeTerms[i]);

var num = i; // to avoid using cycle variable in clojure

// 2) finding rate which would make bond price equal to calculated price
var rt = solver.Solve(rate => EvaluatePrice(rate, num + 1) - price, 0.00, 1.00);
if (rt == null) throw new InvalidOperationException("Didn't converge!");

// 3) Saving result
_treeRates[i] = rt.Value;
}
}

private double EvaluatePrice(double rate, int num) {
var oldPrices = new double[num + 1];
var newPrices = new double[num];
for (var i = 0; i < num + 1; i++) oldPrices[i] = 1;

for (var n = num-1; n >= 0; n--) {
for (var k = 0; k <= n; k++) {
var rt = ((n == num - 1) ? rate : _treeRates[n]) * Math.Exp(-2 * _treeVols[n] * k);
newPrices[n - k] = 0.5*(oldPrices[n-k] + oldPrices[n-k+1])*Math.Exp(-rt);
}
if (n > 0) for (var k = 0; k <= n; k++) oldPrices[k] = newPrices[k];
}
return newPrices[0];
}
}
```