Index: trunk/sources/ALGLIB/ALGLIB License.txt
===================================================================
--- trunk/sources/ALGLIB/ALGLIB License.txt	(revision 2430)
+++ trunk/sources/ALGLIB/ALGLIB License.txt	(revision 2430)
@@ -0,0 +1,16 @@
+﻿Copyright (c) 2003-2009 Sergey Bochkanov (ALGLIB project).
+
+>>> LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
Index: trunk/sources/ALGLIB/ALGLIB.csproj
===================================================================
--- trunk/sources/ALGLIB/ALGLIB.csproj	(revision 2430)
+++ trunk/sources/ALGLIB/ALGLIB.csproj	(revision 2430)
@@ -0,0 +1,125 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{44456535-5DB5-454A-BE2B-7426DA6700B6}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>ALGLIB</RootNamespace>
+    <AssemblyName>ALGLIB</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SignAssembly>true</SignAssembly>
+    <AssemblyOriginatorKeyFile>HeuristicLab.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x86\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <OutputPath>bin\x86\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\x64\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
+    <OutputPath>bin\x64\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Xml.Linq">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data.DataSetExtensions">
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ap.cs" />
+    <Compile Include="bdsvd.cs" />
+    <Compile Include="bidiagonal.cs" />
+    <Compile Include="blas.cs" />
+    <Compile Include="correlation.cs" />
+    <Compile Include="AlglibPlugin.cs">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Compile>
+    <Compile Include="leastsquares.cs" />
+    <Compile Include="lq.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="qr.cs" />
+    <Compile Include="reflections.cs" />
+    <Compile Include="rotations.cs" />
+    <Compile Include="spline3.cs" />
+    <Compile Include="svd.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="ALGLIB License.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\HeuristicLab.PluginInfrastructure\HeuristicLab.PluginInfrastructure.csproj">
+      <Project>{94186A6A-5176-4402-AE83-886557B53CCA}</Project>
+      <Name>HeuristicLab.PluginInfrastructure</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="HeuristicLab.snk" />
+    <None Include="Properties\AssemblyInfo.frame" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <PropertyGroup>
+    <PreBuildEvent>set Path=%25Path%25;$(ProjectDir);$(SolutionDir)
+set ProjectDir=$(ProjectDir)
+set SolutionDir=$(SolutionDir)</PreBuildEvent>
+  </PropertyGroup>
+</Project>
Index: trunk/sources/ALGLIB/AlglibPlugin.cs
===================================================================
--- trunk/sources/ALGLIB/AlglibPlugin.cs	(revision 2430)
+++ trunk/sources/ALGLIB/AlglibPlugin.cs	(revision 2430)
@@ -0,0 +1,34 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2009 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HeuristicLab.PluginInfrastructure;
+
+namespace ALGLIB {
+  [ClassInfo(Name = "ALGLIB")]
+  [PluginFile(Filename = "ALGLIB.dll", Filetype = PluginFileType.Assembly)]
+  [PluginFile(Filename = "ALGLIB License.txt", Filetype = PluginFileType.License)]
+  public class AlgLibPlugin : PluginBase {
+  }
+}
Index: trunk/sources/ALGLIB/Properties/AssemblyInfo.frame
===================================================================
--- trunk/sources/ALGLIB/Properties/AssemblyInfo.frame	(revision 2430)
+++ trunk/sources/ALGLIB/Properties/AssemblyInfo.frame	(revision 2430)
@@ -0,0 +1,59 @@
+#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using HeuristicLab.PluginInfrastructure;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ALGLIB")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ALGLIB")]
+[assembly: AssemblyCopyright("Copyright (c) 2003-2009 Sergey Bochkanov (ALGLIB project)")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9e8c630f-f39d-42ae-97fd-2184b680db31")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("2.1.2.$WCREV$")]
+[assembly: AssemblyFileVersion("2.1.2.$WCREV$")]
+[assembly: AssemblyBuildDate("$WCNOW$")]
Index: trunk/sources/ALGLIB/ap.cs
===================================================================
--- trunk/sources/ALGLIB/ap.cs	(revision 2430)
+++ trunk/sources/ALGLIB/ap.cs	(revision 2430)
@@ -0,0 +1,165 @@
+/*************************************************************************
+AP library
+Copyright (c) 2003-2009 Sergey Bochkanov (ALGLIB project).
+
+>>> LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+namespace AP
+{
+    /********************************************************************
+    Class defining a complex number with double precision.
+    ********************************************************************/
+    public struct Complex
+    {
+        public double x;
+        public double y;
+
+        public Complex(double _x)
+        {
+            x = _x;
+            y = 0;
+        }
+        public Complex(double _x, double _y)
+        {
+            x = _x;
+            y = _y;
+        }
+        public static implicit operator Complex(double _x)
+        {
+            return new Complex(_x);
+        }
+        public static bool operator==(Complex lhs, Complex rhs)
+        {
+            return (lhs.x==rhs.x) & (lhs.y==rhs.y);
+        }
+        public static bool operator!=(Complex lhs, Complex rhs)
+        {
+            return (lhs.x!=rhs.x) | (lhs.y!=rhs.y);
+        }
+        public static Complex operator+(Complex lhs)
+        {
+            return lhs;
+        }
+        public static Complex operator-(Complex lhs)
+        {
+            return new Complex(-lhs.x,-lhs.y);
+        }
+        public static Complex operator+(Complex lhs, Complex rhs)
+        {
+            return new Complex(lhs.x+rhs.x,lhs.y+rhs.y);
+        }
+        public static Complex operator-(Complex lhs, Complex rhs)
+        {
+            return new Complex(lhs.x-rhs.x,lhs.y-rhs.y);
+        }
+        public static Complex operator*(Complex lhs, Complex rhs)
+        { 
+            return new Complex(lhs.x*rhs.x-lhs.y*rhs.y, lhs.x*rhs.y+lhs.y*rhs.x);
+        }
+        public static Complex operator/(Complex lhs, Complex rhs)
+        {
+            Complex result;
+            double e;
+            double f;
+            if( System.Math.Abs(rhs.y)<System.Math.Abs(rhs.x) )
+            {
+                e = rhs.y/rhs.x;
+                f = rhs.x+rhs.y*e;
+                result.x = (lhs.x+lhs.y*e)/f;
+                result.y = (lhs.y-lhs.x*e)/f;
+            }
+            else
+            {
+                e = rhs.x/rhs.y;
+                f = rhs.y+rhs.x*e;
+                result.x = (lhs.y+lhs.x*e)/f;
+                result.y = (-lhs.x+lhs.y*e)/f;
+            }
+            return result;
+        }
+    }    
+    
+	/********************************************************************
+	AP math namespace
+	********************************************************************/
+	public struct rcommstate
+	{
+		public int stage;
+		public int[] ia;
+		public bool[] ba;
+		public double[] ra;
+		public AP.Complex[] ca;
+	};
+
+	/********************************************************************
+    AP math namespace
+    ********************************************************************/
+    public class Math
+    {
+        //public static System.Random RndObject = new System.Random(System.DateTime.Now.Millisecond);
+        public static System.Random RndObject = new System.Random(System.DateTime.Now.Millisecond + 1000*System.DateTime.Now.Second + 60*1000*System.DateTime.Now.Minute);
+
+        public const double MachineEpsilon = 5E-16;
+        public const double MaxRealNumber = 1E300;
+        public const double MinRealNumber = 1E-300;
+        
+        public static double RandomReal()
+        {
+            double r = 0;
+            lock(RndObject){ r = RndObject.NextDouble(); }
+            return r;
+        }
+        public static int RandomInteger(int N)
+        {
+            int r = 0;
+            lock(RndObject){ r = RndObject.Next(N); }
+            return r;
+        }
+        public static double Sqr(double X)
+        {
+            return X*X;
+        }        
+        public static double AbsComplex(Complex z)
+        {
+            double w;
+            double xabs;
+            double yabs;
+            double v;
+    
+            xabs = System.Math.Abs(z.x);
+            yabs = System.Math.Abs(z.y);
+            w = xabs>yabs ? xabs : yabs;
+            v = xabs<yabs ? xabs : yabs; 
+            if( v==0 )
+                return w;
+            else
+            {
+                double t = v/w;
+                return w*System.Math.Sqrt(1+t*t);
+            }
+        }
+        public static Complex Conj(Complex z)
+        {
+            return new Complex(z.x, -z.y); 
+        }    
+        public static Complex CSqr(Complex z)
+        {
+            return new Complex(z.x*z.x-z.y*z.y, 2*z.x*z.y); 
+        }
+
+    }
+}
Index: trunk/sources/ALGLIB/ap.english.html
===================================================================
--- trunk/sources/ALGLIB/ap.english.html	(revision 2430)
+++ trunk/sources/ALGLIB/ap.english.html	(revision 2430)
@@ -0,0 +1,100 @@
+<html>
+<head>
+
+<title>AP Library adapted for C#</title>
+<style type="text/css">
+<!--
+h1 { font-family: Tahoma,sans-serif; font-size : larger; }
+h2 { font-family: Arial,sans-serif; font-size : 11pt; }
+h3 { font-family: Arial,sans-serif; font-size : 9pt; }
+.cond  { color:blue; }
+.const { color:#222222; }
+.func  { color:#111111; }
+-->
+</style>
+</head>
+<body>
+
+<h1>AP Library adapted for C#</h1>
+
+<p align=justify>
+The document describes an AP library adapted for C#. The AP library for C# contains a basic set of mathematical functions and collection classes needed to run the programs from the <a href="http://www.alglib.net/">ALGLIB</a> website.
+</p>
+
+<h1>Compatibility</h1>
+
+<p align=justify>
+This library must be compatible with any C# compiler.
+</p>
+
+<h1>Structure and Use</h1>
+
+<p align=justify>
+The library includes the only file <code>ap.cs</code>
+</p>
+
+<h1>AP Library Description</h1>
+
+<font size=-1>
+<a href="#constants">Constants</a><br>
+<a href="#functions">Functions</a><br>
+<a href="#complex">Complex numbers</a><br>
+</font>
+
+<a name="constants"><h1>Constants</h1></a>
+
+<p align=justify>
+<span class=const>AP.Math.MachineEpsilon</span><br>
+The constant represents the accuracy of machine operations, that is the minimum number for <code>1+machineepsilon&ne;1</code> in the given bit grid. The constant may be taken &quot;oversized&quot;, that is real accuracy can be even higher.
+</p>
+
+<p align=justify>
+<span class=const>AP.Math.MaxRealNumber</span><br>
+The constant represents the highest value of the positive real number, which could be represented on this machine. The constant may be taken &quot;oversized&quot;, that is real boundary can be even higher.
+</p>
+
+<p align=justify>
+<span class=const>AP.Math.MinRealNumber</span><br>
+The constant represents the lowest value of positive real number, which could be represented on this machine. The constant may be taken &quot;oversized&quot;, that is real boundary can be even lower.
+</p>
+
+<a name="functions"><h1>Functions</h1></a>
+
+<p align=justify>
+<span class=func><b>public static double</b> AP.Math.RandomReal()</span><br>
+Returns a random real number from half-interval [0,1).
+</p>
+
+<p align=justify>
+<span class=func><b>public static int</b> AP.Math.RandomInteger(<b>int</b> N)</span><br>
+Returns a random integer between 0 and maxv-1.
+</p>
+
+<p align=justify>
+<span class=func><b>public static double</b> AP.Math.Sqr(<b>double</b> x)</span><br>
+Returns x<sup>2</sup>.
+</p>
+
+<a name="complex"><h1>Class of complex numbers</h1></a>
+
+<p align=justify>
+AP library includes the <code>AP.Complex</code> structure that supports operations with compex numbers. Access to real and imaginary parts of complex number is implemented through the public fields <code>x</code> and <code>y</code>. Arithmetical operations are supported, the same as with embedded data types, by overloading of operations: addition, subtraction, multiplication and division. Addition, subtraction and multiplication are performed by a usual way (i.e., according to their definition which can be found in any textdook in algebra), division is performed using so called &quot;safe&quot; algorithm that could never cause overflow when calculating intermediate results. The library also includes several functions performing elementary operations with complex numbers.
+</p>
+
+<p align=justify>
+<span class=func><b>double</b> AP.Math.AbsComplex(AP.Complex z)</span><br>
+Returns the modulus of complex number z. It should be noted that the modulus calculation is performed using so called &quot;safe&quot; algorithm, that could never cause overflow when calculating intermediate results.
+</p>
+
+<p align=justify>
+<span class=func>AP.Complex AP.Math.Conj(AP.Complex z)</span><br>
+Returns complex conjugate to z.
+</p>
+
+<p align=justify>
+<span class=func>AP.Complex AP.Math.CSqr(AP.Complex z)</span><br>
+Returns the square of z.
+</p>
+
+</body>
+</html>
Index: trunk/sources/ALGLIB/bdsvd.cs
===================================================================
--- trunk/sources/ALGLIB/bdsvd.cs	(revision 2430)
+++ trunk/sources/ALGLIB/bdsvd.cs	(revision 2430)
@@ -0,0 +1,1403 @@
+/*************************************************************************
+Copyright (c) 1992-2007 The University of Tennessee.  All rights reserved.
+
+Contributors:
+    * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to
+      pseudocode.
+
+See subroutines comments for additional copyrights.
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class bdsvd
+    {
+        /*************************************************************************
+        Singular value decomposition of a bidiagonal matrix (extended algorithm)
+
+        The algorithm performs the singular value decomposition  of  a  bidiagonal
+        matrix B (upper or lower) representing it as B = Q*S*P^T, where Q and  P -
+        orthogonal matrices, S - diagonal matrix with non-negative elements on the
+        main diagonal, in descending order.
+
+        The  algorithm  finds  singular  values.  In  addition,  the algorithm can
+        calculate  matrices  Q  and P (more precisely, not the matrices, but their
+        product  with  given  matrices U and VT - U*Q and (P^T)*VT)).  Of  course,
+        matrices U and VT can be of any type, including identity. Furthermore, the
+        algorithm can calculate Q'*C (this product is calculated more  effectively
+        than U*Q,  because  this calculation operates with rows instead  of matrix
+        columns).
+
+        The feature of the algorithm is its ability to find  all  singular  values
+        including those which are arbitrarily close to 0  with  relative  accuracy
+        close to  machine precision. If the parameter IsFractionalAccuracyRequired
+        is set to True, all singular values will have high relative accuracy close
+        to machine precision. If the parameter is set to False, only  the  biggest
+        singular value will have relative accuracy  close  to  machine  precision.
+        The absolute error of other singular values is equal to the absolute error
+        of the biggest singular value.
+
+        Input parameters:
+            D       -   main diagonal of matrix B.
+                        Array whose index ranges within [0..N-1].
+            E       -   superdiagonal (or subdiagonal) of matrix B.
+                        Array whose index ranges within [0..N-2].
+            N       -   size of matrix B.
+            IsUpper -   True, if the matrix is upper bidiagonal.
+            IsFractionalAccuracyRequired -
+                        accuracy to search singular values with.
+            U       -   matrix to be multiplied by Q.
+                        Array whose indexes range within [0..NRU-1, 0..N-1].
+                        The matrix can be bigger, in that case only the  submatrix
+                        [0..NRU-1, 0..N-1] will be multiplied by Q.
+            NRU     -   number of rows in matrix U.
+            C       -   matrix to be multiplied by Q'.
+                        Array whose indexes range within [0..N-1, 0..NCC-1].
+                        The matrix can be bigger, in that case only the  submatrix
+                        [0..N-1, 0..NCC-1] will be multiplied by Q'.
+            NCC     -   number of columns in matrix C.
+            VT      -   matrix to be multiplied by P^T.
+                        Array whose indexes range within [0..N-1, 0..NCVT-1].
+                        The matrix can be bigger, in that case only the  submatrix
+                        [0..N-1, 0..NCVT-1] will be multiplied by P^T.
+            NCVT    -   number of columns in matrix VT.
+
+        Output parameters:
+            D       -   singular values of matrix B in descending order.
+            U       -   if NRU>0, contains matrix U*Q.
+            VT      -   if NCVT>0, contains matrix (P^T)*VT.
+            C       -   if NCC>0, contains matrix Q'*C.
+
+        Result:
+            True, if the algorithm has converged.
+            False, if the algorithm hasn't converged (rare case).
+
+        Additional information:
+            The type of convergence is controlled by the internal  parameter  TOL.
+            If the parameter is greater than 0, the singular values will have
+            relative accuracy TOL. If TOL<0, the singular values will have
+            absolute accuracy ABS(TOL)*norm(B).
+            By default, |TOL| falls within the range of 10*Epsilon and 100*Epsilon,
+            where Epsilon is the machine precision. It is not  recommended  to  use
+            TOL less than 10*Epsilon since this will  considerably  slow  down  the
+            algorithm and may not lead to error decreasing.
+        History:
+            * 31 March, 2007.
+                changed MAXITR from 6 to 12.
+
+          -- LAPACK routine (version 3.0) --
+             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
+             Courant Institute, Argonne National Lab, and Rice University
+             October 31, 1999.
+        *************************************************************************/
+        public static bool rmatrixbdsvd(ref double[] d,
+            double[] e,
+            int n,
+            bool isupper,
+            bool isfractionalaccuracyrequired,
+            ref double[,] u,
+            int nru,
+            ref double[,] c,
+            int ncc,
+            ref double[,] vt,
+            int ncvt)
+        {
+            bool result = new bool();
+            double[] d1 = new double[0];
+            double[] e1 = new double[0];
+            int i_ = 0;
+            int i1_ = 0;
+
+            e = (double[])e.Clone();
+
+            d1 = new double[n+1];
+            i1_ = (0) - (1);
+            for(i_=1; i_<=n;i_++)
+            {
+                d1[i_] = d[i_+i1_];
+            }
+            if( n>1 )
+            {
+                e1 = new double[n-1+1];
+                i1_ = (0) - (1);
+                for(i_=1; i_<=n-1;i_++)
+                {
+                    e1[i_] = e[i_+i1_];
+                }
+            }
+            result = bidiagonalsvddecompositioninternal(ref d1, e1, n, isupper, isfractionalaccuracyrequired, ref u, 0, nru, ref c, 0, ncc, ref vt, 0, ncvt);
+            i1_ = (1) - (0);
+            for(i_=0; i_<=n-1;i_++)
+            {
+                d[i_] = d1[i_+i1_];
+            }
+            return result;
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine. See RMatrixBDSVD for 0-based replacement.
+
+        History:
+            * 31 March, 2007.
+                changed MAXITR from 6 to 12.
+
+          -- LAPACK routine (version 3.0) --
+             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
+             Courant Institute, Argonne National Lab, and Rice University
+             October 31, 1999.
+        *************************************************************************/
+        public static bool bidiagonalsvddecomposition(ref double[] d,
+            double[] e,
+            int n,
+            bool isupper,
+            bool isfractionalaccuracyrequired,
+            ref double[,] u,
+            int nru,
+            ref double[,] c,
+            int ncc,
+            ref double[,] vt,
+            int ncvt)
+        {
+            bool result = new bool();
+
+            e = (double[])e.Clone();
+
+            result = bidiagonalsvddecompositioninternal(ref d, e, n, isupper, isfractionalaccuracyrequired, ref u, 1, nru, ref c, 1, ncc, ref vt, 1, ncvt);
+            return result;
+        }
+
+
+        /*************************************************************************
+        Internal working subroutine for bidiagonal decomposition
+        *************************************************************************/
+        private static bool bidiagonalsvddecompositioninternal(ref double[] d,
+            double[] e,
+            int n,
+            bool isupper,
+            bool isfractionalaccuracyrequired,
+            ref double[,] u,
+            int ustart,
+            int nru,
+            ref double[,] c,
+            int cstart,
+            int ncc,
+            ref double[,] vt,
+            int vstart,
+            int ncvt)
+        {
+            bool result = new bool();
+            int i = 0;
+            int idir = 0;
+            int isub = 0;
+            int iter = 0;
+            int j = 0;
+            int ll = 0;
+            int lll = 0;
+            int m = 0;
+            int maxit = 0;
+            int oldll = 0;
+            int oldm = 0;
+            double abse = 0;
+            double abss = 0;
+            double cosl = 0;
+            double cosr = 0;
+            double cs = 0;
+            double eps = 0;
+            double f = 0;
+            double g = 0;
+            double h = 0;
+            double mu = 0;
+            double oldcs = 0;
+            double oldsn = 0;
+            double r = 0;
+            double shift = 0;
+            double sigmn = 0;
+            double sigmx = 0;
+            double sinl = 0;
+            double sinr = 0;
+            double sll = 0;
+            double smax = 0;
+            double smin = 0;
+            double sminl = 0;
+            double sminlo = 0;
+            double sminoa = 0;
+            double sn = 0;
+            double thresh = 0;
+            double tol = 0;
+            double tolmul = 0;
+            double unfl = 0;
+            double[] work0 = new double[0];
+            double[] work1 = new double[0];
+            double[] work2 = new double[0];
+            double[] work3 = new double[0];
+            int maxitr = 0;
+            bool matrixsplitflag = new bool();
+            bool iterflag = new bool();
+            double[] utemp = new double[0];
+            double[] vttemp = new double[0];
+            double[] ctemp = new double[0];
+            double[] etemp = new double[0];
+            bool rightside = new bool();
+            bool fwddir = new bool();
+            double tmp = 0;
+            int mm1 = 0;
+            int mm0 = 0;
+            bool bchangedir = new bool();
+            int uend = 0;
+            int cend = 0;
+            int vend = 0;
+            int i_ = 0;
+
+            e = (double[])e.Clone();
+
+            result = true;
+            if( n==0 )
+            {
+                return result;
+            }
+            if( n==1 )
+            {
+                if( d[1]<0 )
+                {
+                    d[1] = -d[1];
+                    if( ncvt>0 )
+                    {
+                        for(i_=vstart; i_<=vstart+ncvt-1;i_++)
+                        {
+                            vt[vstart,i_] = -1*vt[vstart,i_];
+                        }
+                    }
+                }
+                return result;
+            }
+            
+            //
+            // init
+            //
+            work0 = new double[n-1+1];
+            work1 = new double[n-1+1];
+            work2 = new double[n-1+1];
+            work3 = new double[n-1+1];
+            uend = ustart+Math.Max(nru-1, 0);
+            vend = vstart+Math.Max(ncvt-1, 0);
+            cend = cstart+Math.Max(ncc-1, 0);
+            utemp = new double[uend+1];
+            vttemp = new double[vend+1];
+            ctemp = new double[cend+1];
+            maxitr = 12;
+            rightside = true;
+            fwddir = true;
+            
+            //
+            // resize E from N-1 to N
+            //
+            etemp = new double[n+1];
+            for(i=1; i<=n-1; i++)
+            {
+                etemp[i] = e[i];
+            }
+            e = new double[n+1];
+            for(i=1; i<=n-1; i++)
+            {
+                e[i] = etemp[i];
+            }
+            e[n] = 0;
+            idir = 0;
+            
+            //
+            // Get machine constants
+            //
+            eps = AP.Math.MachineEpsilon;
+            unfl = AP.Math.MinRealNumber;
+            
+            //
+            // If matrix lower bidiagonal, rotate to be upper bidiagonal
+            // by applying Givens rotations on the left
+            //
+            if( !isupper )
+            {
+                for(i=1; i<=n-1; i++)
+                {
+                    rotations.generaterotation(d[i], e[i], ref cs, ref sn, ref r);
+                    d[i] = r;
+                    e[i] = sn*d[i+1];
+                    d[i+1] = cs*d[i+1];
+                    work0[i] = cs;
+                    work1[i] = sn;
+                }
+                
+                //
+                // Update singular vectors if desired
+                //
+                if( nru>0 )
+                {
+                    rotations.applyrotationsfromtheright(fwddir, ustart, uend, 1+ustart-1, n+ustart-1, ref work0, ref work1, ref u, ref utemp);
+                }
+                if( ncc>0 )
+                {
+                    rotations.applyrotationsfromtheleft(fwddir, 1+cstart-1, n+cstart-1, cstart, cend, ref work0, ref work1, ref c, ref ctemp);
+                }
+            }
+            
+            //
+            // Compute singular values to relative accuracy TOL
+            // (By setting TOL to be negative, algorithm will compute
+            // singular values to absolute accuracy ABS(TOL)*norm(input matrix))
+            //
+            tolmul = Math.Max(10, Math.Min(100, Math.Pow(eps, -0.125)));
+            tol = tolmul*eps;
+            if( !isfractionalaccuracyrequired )
+            {
+                tol = -tol;
+            }
+            
+            //
+            // Compute approximate maximum, minimum singular values
+            //
+            smax = 0;
+            for(i=1; i<=n; i++)
+            {
+                smax = Math.Max(smax, Math.Abs(d[i]));
+            }
+            for(i=1; i<=n-1; i++)
+            {
+                smax = Math.Max(smax, Math.Abs(e[i]));
+            }
+            sminl = 0;
+            if( tol>=0 )
+            {
+                
+                //
+                // Relative accuracy desired
+                //
+                sminoa = Math.Abs(d[1]);
+                if( sminoa!=0 )
+                {
+                    mu = sminoa;
+                    for(i=2; i<=n; i++)
+                    {
+                        mu = Math.Abs(d[i])*(mu/(mu+Math.Abs(e[i-1])));
+                        sminoa = Math.Min(sminoa, mu);
+                        if( sminoa==0 )
+                        {
+                            break;
+                        }
+                    }
+                }
+                sminoa = sminoa/Math.Sqrt(n);
+                thresh = Math.Max(tol*sminoa, maxitr*n*n*unfl);
+            }
+            else
+            {
+                
+                //
+                // Absolute accuracy desired
+                //
+                thresh = Math.Max(Math.Abs(tol)*smax, maxitr*n*n*unfl);
+            }
+            
+            //
+            // Prepare for main iteration loop for the singular values
+            // (MAXIT is the maximum number of passes through the inner
+            // loop permitted before nonconvergence signalled.)
+            //
+            maxit = maxitr*n*n;
+            iter = 0;
+            oldll = -1;
+            oldm = -1;
+            
+            //
+            // M points to last element of unconverged part of matrix
+            //
+            m = n;
+            
+            //
+            // Begin main iteration loop
+            //
+            while( true )
+            {
+                
+                //
+                // Check for convergence or exceeding iteration count
+                //
+                if( m<=1 )
+                {
+                    break;
+                }
+                if( iter>maxit )
+                {
+                    result = false;
+                    return result;
+                }
+                
+                //
+                // Find diagonal block of matrix to work on
+                //
+                if( tol<0 & Math.Abs(d[m])<=thresh )
+                {
+                    d[m] = 0;
+                }
+                smax = Math.Abs(d[m]);
+                smin = smax;
+                matrixsplitflag = false;
+                for(lll=1; lll<=m-1; lll++)
+                {
+                    ll = m-lll;
+                    abss = Math.Abs(d[ll]);
+                    abse = Math.Abs(e[ll]);
+                    if( tol<0 & abss<=thresh )
+                    {
+                        d[ll] = 0;
+                    }
+                    if( abse<=thresh )
+                    {
+                        matrixsplitflag = true;
+                        break;
+                    }
+                    smin = Math.Min(smin, abss);
+                    smax = Math.Max(smax, Math.Max(abss, abse));
+                }
+                if( !matrixsplitflag )
+                {
+                    ll = 0;
+                }
+                else
+                {
+                    
+                    //
+                    // Matrix splits since E(LL) = 0
+                    //
+                    e[ll] = 0;
+                    if( ll==m-1 )
+                    {
+                        
+                        //
+                        // Convergence of bottom singular value, return to top of loop
+                        //
+                        m = m-1;
+                        continue;
+                    }
+                }
+                ll = ll+1;
+                
+                //
+                // E(LL) through E(M-1) are nonzero, E(LL-1) is zero
+                //
+                if( ll==m-1 )
+                {
+                    
+                    //
+                    // 2 by 2 block, handle separately
+                    //
+                    svdv2x2(d[m-1], e[m-1], d[m], ref sigmn, ref sigmx, ref sinr, ref cosr, ref sinl, ref cosl);
+                    d[m-1] = sigmx;
+                    e[m-1] = 0;
+                    d[m] = sigmn;
+                    
+                    //
+                    // Compute singular vectors, if desired
+                    //
+                    if( ncvt>0 )
+                    {
+                        mm0 = m+(vstart-1);
+                        mm1 = m-1+(vstart-1);
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vttemp[i_] = cosr*vt[mm1,i_];
+                        }
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vttemp[i_] = vttemp[i_] + sinr*vt[mm0,i_];
+                        }
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vt[mm0,i_] = cosr*vt[mm0,i_];
+                        }
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vt[mm0,i_] = vt[mm0,i_] - sinr*vt[mm1,i_];
+                        }
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vt[mm1,i_] = vttemp[i_];
+                        }
+                    }
+                    if( nru>0 )
+                    {
+                        mm0 = m+ustart-1;
+                        mm1 = m-1+ustart-1;
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            utemp[i_] = cosl*u[i_,mm1];
+                        }
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            utemp[i_] = utemp[i_] + sinl*u[i_,mm0];
+                        }
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            u[i_,mm0] = cosl*u[i_,mm0];
+                        }
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            u[i_,mm0] = u[i_,mm0] - sinl*u[i_,mm1];
+                        }
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            u[i_,mm1] = utemp[i_];
+                        }
+                    }
+                    if( ncc>0 )
+                    {
+                        mm0 = m+cstart-1;
+                        mm1 = m-1+cstart-1;
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            ctemp[i_] = cosl*c[mm1,i_];
+                        }
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            ctemp[i_] = ctemp[i_] + sinl*c[mm0,i_];
+                        }
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            c[mm0,i_] = cosl*c[mm0,i_];
+                        }
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            c[mm0,i_] = c[mm0,i_] - sinl*c[mm1,i_];
+                        }
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            c[mm1,i_] = ctemp[i_];
+                        }
+                    }
+                    m = m-2;
+                    continue;
+                }
+                
+                //
+                // If working on new submatrix, choose shift direction
+                // (from larger end diagonal element towards smaller)
+                //
+                // Previously was
+                //     "if (LL>OLDM) or (M<OLDLL) then"
+                // fixed thanks to Michael Rolle < m@rolle.name >
+                // Very strange that LAPACK still contains it.
+                //
+                bchangedir = false;
+                if( idir==1 & Math.Abs(d[ll])<1.0E-3*Math.Abs(d[m]) )
+                {
+                    bchangedir = true;
+                }
+                if( idir==2 & Math.Abs(d[m])<1.0E-3*Math.Abs(d[ll]) )
+                {
+                    bchangedir = true;
+                }
+                if( ll!=oldll | m!=oldm | bchangedir )
+                {
+                    if( Math.Abs(d[ll])>=Math.Abs(d[m]) )
+                    {
+                        
+                        //
+                        // Chase bulge from top (big end) to bottom (small end)
+                        //
+                        idir = 1;
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Chase bulge from bottom (big end) to top (small end)
+                        //
+                        idir = 2;
+                    }
+                }
+                
+                //
+                // Apply convergence tests
+                //
+                if( idir==1 )
+                {
+                    
+                    //
+                    // Run convergence test in forward direction
+                    // First apply standard test to bottom of matrix
+                    //
+                    if( Math.Abs(e[m-1])<=Math.Abs(tol)*Math.Abs(d[m]) | tol<0 & Math.Abs(e[m-1])<=thresh )
+                    {
+                        e[m-1] = 0;
+                        continue;
+                    }
+                    if( tol>=0 )
+                    {
+                        
+                        //
+                        // If relative accuracy desired,
+                        // apply convergence criterion forward
+                        //
+                        mu = Math.Abs(d[ll]);
+                        sminl = mu;
+                        iterflag = false;
+                        for(lll=ll; lll<=m-1; lll++)
+                        {
+                            if( Math.Abs(e[lll])<=tol*mu )
+                            {
+                                e[lll] = 0;
+                                iterflag = true;
+                                break;
+                            }
+                            sminlo = sminl;
+                            mu = Math.Abs(d[lll+1])*(mu/(mu+Math.Abs(e[lll])));
+                            sminl = Math.Min(sminl, mu);
+                        }
+                        if( iterflag )
+                        {
+                            continue;
+                        }
+                    }
+                }
+                else
+                {
+                    
+                    //
+                    // Run convergence test in backward direction
+                    // First apply standard test to top of matrix
+                    //
+                    if( Math.Abs(e[ll])<=Math.Abs(tol)*Math.Abs(d[ll]) | tol<0 & Math.Abs(e[ll])<=thresh )
+                    {
+                        e[ll] = 0;
+                        continue;
+                    }
+                    if( tol>=0 )
+                    {
+                        
+                        //
+                        // If relative accuracy desired,
+                        // apply convergence criterion backward
+                        //
+                        mu = Math.Abs(d[m]);
+                        sminl = mu;
+                        iterflag = false;
+                        for(lll=m-1; lll>=ll; lll--)
+                        {
+                            if( Math.Abs(e[lll])<=tol*mu )
+                            {
+                                e[lll] = 0;
+                                iterflag = true;
+                                break;
+                            }
+                            sminlo = sminl;
+                            mu = Math.Abs(d[lll])*(mu/(mu+Math.Abs(e[lll])));
+                            sminl = Math.Min(sminl, mu);
+                        }
+                        if( iterflag )
+                        {
+                            continue;
+                        }
+                    }
+                }
+                oldll = ll;
+                oldm = m;
+                
+                //
+                // Compute shift.  First, test if shifting would ruin relative
+                // accuracy, and if so set the shift to zero.
+                //
+                if( tol>=0 & n*tol*(sminl/smax)<=Math.Max(eps, 0.01*tol) )
+                {
+                    
+                    //
+                    // Use a zero shift to avoid loss of relative accuracy
+                    //
+                    shift = 0;
+                }
+                else
+                {
+                    
+                    //
+                    // Compute the shift from 2-by-2 block at end of matrix
+                    //
+                    if( idir==1 )
+                    {
+                        sll = Math.Abs(d[ll]);
+                        svd2x2(d[m-1], e[m-1], d[m], ref shift, ref r);
+                    }
+                    else
+                    {
+                        sll = Math.Abs(d[m]);
+                        svd2x2(d[ll], e[ll], d[ll+1], ref shift, ref r);
+                    }
+                    
+                    //
+                    // Test if shift negligible, and if so set to zero
+                    //
+                    if( sll>0 )
+                    {
+                        if( AP.Math.Sqr(shift/sll)<eps )
+                        {
+                            shift = 0;
+                        }
+                    }
+                }
+                
+                //
+                // Increment iteration count
+                //
+                iter = iter+m-ll;
+                
+                //
+                // If SHIFT = 0, do simplified QR iteration
+                //
+                if( shift==0 )
+                {
+                    if( idir==1 )
+                    {
+                        
+                        //
+                        // Chase bulge from top to bottom
+                        // Save cosines and sines for later singular vector updates
+                        //
+                        cs = 1;
+                        oldcs = 1;
+                        for(i=ll; i<=m-1; i++)
+                        {
+                            rotations.generaterotation(d[i]*cs, e[i], ref cs, ref sn, ref r);
+                            if( i>ll )
+                            {
+                                e[i-1] = oldsn*r;
+                            }
+                            rotations.generaterotation(oldcs*r, d[i+1]*sn, ref oldcs, ref oldsn, ref tmp);
+                            d[i] = tmp;
+                            work0[i-ll+1] = cs;
+                            work1[i-ll+1] = sn;
+                            work2[i-ll+1] = oldcs;
+                            work3[i-ll+1] = oldsn;
+                        }
+                        h = d[m]*cs;
+                        d[m] = h*oldcs;
+                        e[m-1] = h*oldsn;
+                        
+                        //
+                        // Update singular vectors
+                        //
+                        if( ncvt>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(fwddir, ll+vstart-1, m+vstart-1, vstart, vend, ref work0, ref work1, ref vt, ref vttemp);
+                        }
+                        if( nru>0 )
+                        {
+                            rotations.applyrotationsfromtheright(fwddir, ustart, uend, ll+ustart-1, m+ustart-1, ref work2, ref work3, ref u, ref utemp);
+                        }
+                        if( ncc>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(fwddir, ll+cstart-1, m+cstart-1, cstart, cend, ref work2, ref work3, ref c, ref ctemp);
+                        }
+                        
+                        //
+                        // Test convergence
+                        //
+                        if( Math.Abs(e[m-1])<=thresh )
+                        {
+                            e[m-1] = 0;
+                        }
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Chase bulge from bottom to top
+                        // Save cosines and sines for later singular vector updates
+                        //
+                        cs = 1;
+                        oldcs = 1;
+                        for(i=m; i>=ll+1; i--)
+                        {
+                            rotations.generaterotation(d[i]*cs, e[i-1], ref cs, ref sn, ref r);
+                            if( i<m )
+                            {
+                                e[i] = oldsn*r;
+                            }
+                            rotations.generaterotation(oldcs*r, d[i-1]*sn, ref oldcs, ref oldsn, ref tmp);
+                            d[i] = tmp;
+                            work0[i-ll] = cs;
+                            work1[i-ll] = -sn;
+                            work2[i-ll] = oldcs;
+                            work3[i-ll] = -oldsn;
+                        }
+                        h = d[ll]*cs;
+                        d[ll] = h*oldcs;
+                        e[ll] = h*oldsn;
+                        
+                        //
+                        // Update singular vectors
+                        //
+                        if( ncvt>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(!fwddir, ll+vstart-1, m+vstart-1, vstart, vend, ref work2, ref work3, ref vt, ref vttemp);
+                        }
+                        if( nru>0 )
+                        {
+                            rotations.applyrotationsfromtheright(!fwddir, ustart, uend, ll+ustart-1, m+ustart-1, ref work0, ref work1, ref u, ref utemp);
+                        }
+                        if( ncc>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(!fwddir, ll+cstart-1, m+cstart-1, cstart, cend, ref work0, ref work1, ref c, ref ctemp);
+                        }
+                        
+                        //
+                        // Test convergence
+                        //
+                        if( Math.Abs(e[ll])<=thresh )
+                        {
+                            e[ll] = 0;
+                        }
+                    }
+                }
+                else
+                {
+                    
+                    //
+                    // Use nonzero shift
+                    //
+                    if( idir==1 )
+                    {
+                        
+                        //
+                        // Chase bulge from top to bottom
+                        // Save cosines and sines for later singular vector updates
+                        //
+                        f = (Math.Abs(d[ll])-shift)*(extsignbdsqr(1, d[ll])+shift/d[ll]);
+                        g = e[ll];
+                        for(i=ll; i<=m-1; i++)
+                        {
+                            rotations.generaterotation(f, g, ref cosr, ref sinr, ref r);
+                            if( i>ll )
+                            {
+                                e[i-1] = r;
+                            }
+                            f = cosr*d[i]+sinr*e[i];
+                            e[i] = cosr*e[i]-sinr*d[i];
+                            g = sinr*d[i+1];
+                            d[i+1] = cosr*d[i+1];
+                            rotations.generaterotation(f, g, ref cosl, ref sinl, ref r);
+                            d[i] = r;
+                            f = cosl*e[i]+sinl*d[i+1];
+                            d[i+1] = cosl*d[i+1]-sinl*e[i];
+                            if( i<m-1 )
+                            {
+                                g = sinl*e[i+1];
+                                e[i+1] = cosl*e[i+1];
+                            }
+                            work0[i-ll+1] = cosr;
+                            work1[i-ll+1] = sinr;
+                            work2[i-ll+1] = cosl;
+                            work3[i-ll+1] = sinl;
+                        }
+                        e[m-1] = f;
+                        
+                        //
+                        // Update singular vectors
+                        //
+                        if( ncvt>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(fwddir, ll+vstart-1, m+vstart-1, vstart, vend, ref work0, ref work1, ref vt, ref vttemp);
+                        }
+                        if( nru>0 )
+                        {
+                            rotations.applyrotationsfromtheright(fwddir, ustart, uend, ll+ustart-1, m+ustart-1, ref work2, ref work3, ref u, ref utemp);
+                        }
+                        if( ncc>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(fwddir, ll+cstart-1, m+cstart-1, cstart, cend, ref work2, ref work3, ref c, ref ctemp);
+                        }
+                        
+                        //
+                        // Test convergence
+                        //
+                        if( Math.Abs(e[m-1])<=thresh )
+                        {
+                            e[m-1] = 0;
+                        }
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Chase bulge from bottom to top
+                        // Save cosines and sines for later singular vector updates
+                        //
+                        f = (Math.Abs(d[m])-shift)*(extsignbdsqr(1, d[m])+shift/d[m]);
+                        g = e[m-1];
+                        for(i=m; i>=ll+1; i--)
+                        {
+                            rotations.generaterotation(f, g, ref cosr, ref sinr, ref r);
+                            if( i<m )
+                            {
+                                e[i] = r;
+                            }
+                            f = cosr*d[i]+sinr*e[i-1];
+                            e[i-1] = cosr*e[i-1]-sinr*d[i];
+                            g = sinr*d[i-1];
+                            d[i-1] = cosr*d[i-1];
+                            rotations.generaterotation(f, g, ref cosl, ref sinl, ref r);
+                            d[i] = r;
+                            f = cosl*e[i-1]+sinl*d[i-1];
+                            d[i-1] = cosl*d[i-1]-sinl*e[i-1];
+                            if( i>ll+1 )
+                            {
+                                g = sinl*e[i-2];
+                                e[i-2] = cosl*e[i-2];
+                            }
+                            work0[i-ll] = cosr;
+                            work1[i-ll] = -sinr;
+                            work2[i-ll] = cosl;
+                            work3[i-ll] = -sinl;
+                        }
+                        e[ll] = f;
+                        
+                        //
+                        // Test convergence
+                        //
+                        if( Math.Abs(e[ll])<=thresh )
+                        {
+                            e[ll] = 0;
+                        }
+                        
+                        //
+                        // Update singular vectors if desired
+                        //
+                        if( ncvt>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(!fwddir, ll+vstart-1, m+vstart-1, vstart, vend, ref work2, ref work3, ref vt, ref vttemp);
+                        }
+                        if( nru>0 )
+                        {
+                            rotations.applyrotationsfromtheright(!fwddir, ustart, uend, ll+ustart-1, m+ustart-1, ref work0, ref work1, ref u, ref utemp);
+                        }
+                        if( ncc>0 )
+                        {
+                            rotations.applyrotationsfromtheleft(!fwddir, ll+cstart-1, m+cstart-1, cstart, cend, ref work0, ref work1, ref c, ref ctemp);
+                        }
+                    }
+                }
+                
+                //
+                // QR iteration finished, go back and check convergence
+                //
+                continue;
+            }
+            
+            //
+            // All singular values converged, so make them positive
+            //
+            for(i=1; i<=n; i++)
+            {
+                if( d[i]<0 )
+                {
+                    d[i] = -d[i];
+                    
+                    //
+                    // Change sign of singular vectors, if desired
+                    //
+                    if( ncvt>0 )
+                    {
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vt[i+vstart-1,i_] = -1*vt[i+vstart-1,i_];
+                        }
+                    }
+                }
+            }
+            
+            //
+            // Sort the singular values into decreasing order (insertion sort on
+            // singular values, but only one transposition per singular vector)
+            //
+            for(i=1; i<=n-1; i++)
+            {
+                
+                //
+                // Scan for smallest D(I)
+                //
+                isub = 1;
+                smin = d[1];
+                for(j=2; j<=n+1-i; j++)
+                {
+                    if( d[j]<=smin )
+                    {
+                        isub = j;
+                        smin = d[j];
+                    }
+                }
+                if( isub!=n+1-i )
+                {
+                    
+                    //
+                    // Swap singular values and vectors
+                    //
+                    d[isub] = d[n+1-i];
+                    d[n+1-i] = smin;
+                    if( ncvt>0 )
+                    {
+                        j = n+1-i;
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vttemp[i_] = vt[isub+vstart-1,i_];
+                        }
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vt[isub+vstart-1,i_] = vt[j+vstart-1,i_];
+                        }
+                        for(i_=vstart; i_<=vend;i_++)
+                        {
+                            vt[j+vstart-1,i_] = vttemp[i_];
+                        }
+                    }
+                    if( nru>0 )
+                    {
+                        j = n+1-i;
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            utemp[i_] = u[i_,isub+ustart-1];
+                        }
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            u[i_,isub+ustart-1] = u[i_,j+ustart-1];
+                        }
+                        for(i_=ustart; i_<=uend;i_++)
+                        {
+                            u[i_,j+ustart-1] = utemp[i_];
+                        }
+                    }
+                    if( ncc>0 )
+                    {
+                        j = n+1-i;
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            ctemp[i_] = c[isub+cstart-1,i_];
+                        }
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            c[isub+cstart-1,i_] = c[j+cstart-1,i_];
+                        }
+                        for(i_=cstart; i_<=cend;i_++)
+                        {
+                            c[j+cstart-1,i_] = ctemp[i_];
+                        }
+                    }
+                }
+            }
+            return result;
+        }
+
+
+        private static double extsignbdsqr(double a,
+            double b)
+        {
+            double result = 0;
+
+            if( b>=0 )
+            {
+                result = Math.Abs(a);
+            }
+            else
+            {
+                result = -Math.Abs(a);
+            }
+            return result;
+        }
+
+
+        private static void svd2x2(double f,
+            double g,
+            double h,
+            ref double ssmin,
+            ref double ssmax)
+        {
+            double aas = 0;
+            double at = 0;
+            double au = 0;
+            double c = 0;
+            double fa = 0;
+            double fhmn = 0;
+            double fhmx = 0;
+            double ga = 0;
+            double ha = 0;
+
+            fa = Math.Abs(f);
+            ga = Math.Abs(g);
+            ha = Math.Abs(h);
+            fhmn = Math.Min(fa, ha);
+            fhmx = Math.Max(fa, ha);
+            if( fhmn==0 )
+            {
+                ssmin = 0;
+                if( fhmx==0 )
+                {
+                    ssmax = ga;
+                }
+                else
+                {
+                    ssmax = Math.Max(fhmx, ga)*Math.Sqrt(1+AP.Math.Sqr(Math.Min(fhmx, ga)/Math.Max(fhmx, ga)));
+                }
+            }
+            else
+            {
+                if( ga<fhmx )
+                {
+                    aas = 1+fhmn/fhmx;
+                    at = (fhmx-fhmn)/fhmx;
+                    au = AP.Math.Sqr(ga/fhmx);
+                    c = 2/(Math.Sqrt(aas*aas+au)+Math.Sqrt(at*at+au));
+                    ssmin = fhmn*c;
+                    ssmax = fhmx/c;
+                }
+                else
+                {
+                    au = fhmx/ga;
+                    if( au==0 )
+                    {
+                        
+                        //
+                        // Avoid possible harmful underflow if exponent range
+                        // asymmetric (true SSMIN may not underflow even if
+                        // AU underflows)
+                        //
+                        ssmin = fhmn*fhmx/ga;
+                        ssmax = ga;
+                    }
+                    else
+                    {
+                        aas = 1+fhmn/fhmx;
+                        at = (fhmx-fhmn)/fhmx;
+                        c = 1/(Math.Sqrt(1+AP.Math.Sqr(aas*au))+Math.Sqrt(1+AP.Math.Sqr(at*au)));
+                        ssmin = fhmn*c*au;
+                        ssmin = ssmin+ssmin;
+                        ssmax = ga/(c+c);
+                    }
+                }
+            }
+        }
+
+
+        private static void svdv2x2(double f,
+            double g,
+            double h,
+            ref double ssmin,
+            ref double ssmax,
+            ref double snr,
+            ref double csr,
+            ref double snl,
+            ref double csl)
+        {
+            bool gasmal = new bool();
+            bool swp = new bool();
+            int pmax = 0;
+            double a = 0;
+            double clt = 0;
+            double crt = 0;
+            double d = 0;
+            double fa = 0;
+            double ft = 0;
+            double ga = 0;
+            double gt = 0;
+            double ha = 0;
+            double ht = 0;
+            double l = 0;
+            double m = 0;
+            double mm = 0;
+            double r = 0;
+            double s = 0;
+            double slt = 0;
+            double srt = 0;
+            double t = 0;
+            double temp = 0;
+            double tsign = 0;
+            double tt = 0;
+            double v = 0;
+
+            ft = f;
+            fa = Math.Abs(ft);
+            ht = h;
+            ha = Math.Abs(h);
+            
+            //
+            // PMAX points to the maximum absolute element of matrix
+            //  PMAX = 1 if F largest in absolute values
+            //  PMAX = 2 if G largest in absolute values
+            //  PMAX = 3 if H largest in absolute values
+            //
+            pmax = 1;
+            swp = ha>fa;
+            if( swp )
+            {
+                
+                //
+                // Now FA .ge. HA
+                //
+                pmax = 3;
+                temp = ft;
+                ft = ht;
+                ht = temp;
+                temp = fa;
+                fa = ha;
+                ha = temp;
+            }
+            gt = g;
+            ga = Math.Abs(gt);
+            if( ga==0 )
+            {
+                
+                //
+                // Diagonal matrix
+                //
+                ssmin = ha;
+                ssmax = fa;
+                clt = 1;
+                crt = 1;
+                slt = 0;
+                srt = 0;
+            }
+            else
+            {
+                gasmal = true;
+                if( ga>fa )
+                {
+                    pmax = 2;
+                    if( fa/ga<AP.Math.MachineEpsilon )
+                    {
+                        
+                        //
+                        // Case of very large GA
+                        //
+                        gasmal = false;
+                        ssmax = ga;
+                        if( ha>1 )
+                        {
+                            v = ga/ha;
+                            ssmin = fa/v;
+                        }
+                        else
+                        {
+                            v = fa/ga;
+                            ssmin = v*ha;
+                        }
+                        clt = 1;
+                        slt = ht/gt;
+                        srt = 1;
+                        crt = ft/gt;
+                    }
+                }
+                if( gasmal )
+                {
+                    
+                    //
+                    // Normal case
+                    //
+                    d = fa-ha;
+                    if( d==fa )
+                    {
+                        l = 1;
+                    }
+                    else
+                    {
+                        l = d/fa;
+                    }
+                    m = gt/ft;
+                    t = 2-l;
+                    mm = m*m;
+                    tt = t*t;
+                    s = Math.Sqrt(tt+mm);
+                    if( l==0 )
+                    {
+                        r = Math.Abs(m);
+                    }
+                    else
+                    {
+                        r = Math.Sqrt(l*l+mm);
+                    }
+                    a = 0.5*(s+r);
+                    ssmin = ha/a;
+                    ssmax = fa*a;
+                    if( mm==0 )
+                    {
+                        
+                        //
+                        // Note that M is very tiny
+                        //
+                        if( l==0 )
+                        {
+                            t = extsignbdsqr(2, ft)*extsignbdsqr(1, gt);
+                        }
+                        else
+                        {
+                            t = gt/extsignbdsqr(d, ft)+m/t;
+                        }
+                    }
+                    else
+                    {
+                        t = (m/(s+t)+m/(r+l))*(1+a);
+                    }
+                    l = Math.Sqrt(t*t+4);
+                    crt = 2/l;
+                    srt = t/l;
+                    clt = (crt+srt*m)/a;
+                    v = ht/ft;
+                    slt = v*srt/a;
+                }
+            }
+            if( swp )
+            {
+                csl = srt;
+                snl = crt;
+                csr = slt;
+                snr = clt;
+            }
+            else
+            {
+                csl = clt;
+                snl = slt;
+                csr = crt;
+                snr = srt;
+            }
+            
+            //
+            // Correct signs of SSMAX and SSMIN
+            //
+            if( pmax==1 )
+            {
+                tsign = extsignbdsqr(1, csr)*extsignbdsqr(1, csl)*extsignbdsqr(1, f);
+            }
+            if( pmax==2 )
+            {
+                tsign = extsignbdsqr(1, snr)*extsignbdsqr(1, csl)*extsignbdsqr(1, g);
+            }
+            if( pmax==3 )
+            {
+                tsign = extsignbdsqr(1, snr)*extsignbdsqr(1, snl)*extsignbdsqr(1, h);
+            }
+            ssmax = extsignbdsqr(ssmax, tsign);
+            ssmin = extsignbdsqr(ssmin, tsign*extsignbdsqr(1, f)*extsignbdsqr(1, h));
+        }
+    }
+}
Index: trunk/sources/ALGLIB/bidiagonal.cs
===================================================================
--- trunk/sources/ALGLIB/bidiagonal.cs	(revision 2430)
+++ trunk/sources/ALGLIB/bidiagonal.cs	(revision 2430)
@@ -0,0 +1,1446 @@
+/*************************************************************************
+Copyright (c) 1992-2007 The University of Tennessee.  All rights reserved.
+
+Contributors:
+    * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to
+      pseudocode.
+
+See subroutines comments for additional copyrights.
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class bidiagonal
+    {
+        /*************************************************************************
+        Reduction of a rectangular matrix to  bidiagonal form
+
+        The algorithm reduces the rectangular matrix A to  bidiagonal form by
+        orthogonal transformations P and Q: A = Q*B*P.
+
+        Input parameters:
+            A       -   source matrix. array[0..M-1, 0..N-1]
+            M       -   number of rows in matrix A.
+            N       -   number of columns in matrix A.
+
+        Output parameters:
+            A       -   matrices Q, B, P in compact form (see below).
+            TauQ    -   scalar factors which are used to form matrix Q.
+            TauP    -   scalar factors which are used to form matrix P.
+
+        The main diagonal and one of the  secondary  diagonals  of  matrix  A  are
+        replaced with bidiagonal  matrix  B.  Other  elements  contain  elementary
+        reflections which form MxM matrix Q and NxN matrix P, respectively.
+
+        If M>=N, B is the upper  bidiagonal  MxN  matrix  and  is  stored  in  the
+        corresponding  elements  of  matrix  A.  Matrix  Q  is  represented  as  a
+        product   of   elementary   reflections   Q = H(0)*H(1)*...*H(n-1),  where
+        H(i) = 1-tau*v*v'. Here tau is a scalar which is stored  in  TauQ[i],  and
+        vector v has the following  structure:  v(0:i-1)=0, v(i)=1, v(i+1:m-1)  is
+        stored   in   elements   A(i+1:m-1,i).   Matrix   P  is  as  follows:  P =
+        G(0)*G(1)*...*G(n-2), where G(i) = 1 - tau*u*u'. Tau is stored in TauP[i],
+        u(0:i)=0, u(i+1)=1, u(i+2:n-1) is stored in elements A(i,i+2:n-1).
+
+        If M<N, B is the  lower  bidiagonal  MxN  matrix  and  is  stored  in  the
+        corresponding   elements  of  matrix  A.  Q = H(0)*H(1)*...*H(m-2),  where
+        H(i) = 1 - tau*v*v', tau is stored in TauQ, v(0:i)=0, v(i+1)=1, v(i+2:m-1)
+        is    stored    in   elements   A(i+2:m-1,i).    P = G(0)*G(1)*...*G(m-1),
+        G(i) = 1-tau*u*u', tau is stored in  TauP,  u(0:i-1)=0, u(i)=1, u(i+1:n-1)
+        is stored in A(i,i+1:n-1).
+
+        EXAMPLE:
+
+        m=6, n=5 (m > n):               m=5, n=6 (m < n):
+
+        (  d   e   u1  u1  u1 )         (  d   u1  u1  u1  u1  u1 )
+        (  v1  d   e   u2  u2 )         (  e   d   u2  u2  u2  u2 )
+        (  v1  v2  d   e   u3 )         (  v1  e   d   u3  u3  u3 )
+        (  v1  v2  v3  d   e  )         (  v1  v2  e   d   u4  u4 )
+        (  v1  v2  v3  v4  d  )         (  v1  v2  v3  e   d   u5 )
+        (  v1  v2  v3  v4  v5 )
+
+        Here vi and ui are vectors which form H(i) and G(i), and d and e -
+        are the diagonal and off-diagonal elements of matrix B.
+        *************************************************************************/
+        public static void rmatrixbd(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tauq,
+            ref double[] taup)
+        {
+            double[] work = new double[0];
+            double[] t = new double[0];
+            int minmn = 0;
+            int maxmn = 0;
+            int i = 0;
+            int j = 0;
+            double ltau = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            
+            //
+            // Prepare
+            //
+            if( n<=0 | m<=0 )
+            {
+                return;
+            }
+            minmn = Math.Min(m, n);
+            maxmn = Math.Max(m, n);
+            work = new double[maxmn+1];
+            t = new double[maxmn+1];
+            if( m>=n )
+            {
+                tauq = new double[n-1+1];
+                taup = new double[n-1+1];
+            }
+            else
+            {
+                tauq = new double[m-1+1];
+                taup = new double[m-1+1];
+            }
+            if( m>=n )
+            {
+                
+                //
+                // Reduce to upper bidiagonal form
+                //
+                for(i=0; i<=n-1; i++)
+                {
+                    
+                    //
+                    // Generate elementary reflector H(i) to annihilate A(i+1:m-1,i)
+                    //
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=m-i;i_++)
+                    {
+                        t[i_] = a[i_+i1_,i];
+                    }
+                    reflections.generatereflection(ref t, m-i, ref ltau);
+                    tauq[i] = ltau;
+                    i1_ = (1) - (i);
+                    for(i_=i; i_<=m-1;i_++)
+                    {
+                        a[i_,i] = t[i_+i1_];
+                    }
+                    t[1] = 1;
+                    
+                    //
+                    // Apply H(i) to A(i:m-1,i+1:n-1) from the left
+                    //
+                    reflections.applyreflectionfromtheleft(ref a, ltau, ref t, i, m-1, i+1, n-1, ref work);
+                    if( i<n-1 )
+                    {
+                        
+                        //
+                        // Generate elementary reflector G(i) to annihilate
+                        // A(i,i+2:n-1)
+                        //
+                        i1_ = (i+1) - (1);
+                        for(i_=1; i_<=n-i-1;i_++)
+                        {
+                            t[i_] = a[i,i_+i1_];
+                        }
+                        reflections.generatereflection(ref t, n-1-i, ref ltau);
+                        taup[i] = ltau;
+                        i1_ = (1) - (i+1);
+                        for(i_=i+1; i_<=n-1;i_++)
+                        {
+                            a[i,i_] = t[i_+i1_];
+                        }
+                        t[1] = 1;
+                        
+                        //
+                        // Apply G(i) to A(i+1:m-1,i+1:n-1) from the right
+                        //
+                        reflections.applyreflectionfromtheright(ref a, ltau, ref t, i+1, m-1, i+1, n-1, ref work);
+                    }
+                    else
+                    {
+                        taup[i] = 0;
+                    }
+                }
+            }
+            else
+            {
+                
+                //
+                // Reduce to lower bidiagonal form
+                //
+                for(i=0; i<=m-1; i++)
+                {
+                    
+                    //
+                    // Generate elementary reflector G(i) to annihilate A(i,i+1:n-1)
+                    //
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=n-i;i_++)
+                    {
+                        t[i_] = a[i,i_+i1_];
+                    }
+                    reflections.generatereflection(ref t, n-i, ref ltau);
+                    taup[i] = ltau;
+                    i1_ = (1) - (i);
+                    for(i_=i; i_<=n-1;i_++)
+                    {
+                        a[i,i_] = t[i_+i1_];
+                    }
+                    t[1] = 1;
+                    
+                    //
+                    // Apply G(i) to A(i+1:m-1,i:n-1) from the right
+                    //
+                    reflections.applyreflectionfromtheright(ref a, ltau, ref t, i+1, m-1, i, n-1, ref work);
+                    if( i<m-1 )
+                    {
+                        
+                        //
+                        // Generate elementary reflector H(i) to annihilate
+                        // A(i+2:m-1,i)
+                        //
+                        i1_ = (i+1) - (1);
+                        for(i_=1; i_<=m-1-i;i_++)
+                        {
+                            t[i_] = a[i_+i1_,i];
+                        }
+                        reflections.generatereflection(ref t, m-1-i, ref ltau);
+                        tauq[i] = ltau;
+                        i1_ = (1) - (i+1);
+                        for(i_=i+1; i_<=m-1;i_++)
+                        {
+                            a[i_,i] = t[i_+i1_];
+                        }
+                        t[1] = 1;
+                        
+                        //
+                        // Apply H(i) to A(i+1:m-1,i+1:n-1) from the left
+                        //
+                        reflections.applyreflectionfromtheleft(ref a, ltau, ref t, i+1, m-1, i+1, n-1, ref work);
+                    }
+                    else
+                    {
+                        tauq[i] = 0;
+                    }
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Unpacking matrix Q which reduces a matrix to bidiagonal form.
+
+        Input parameters:
+            QP          -   matrices Q and P in compact form.
+                            Output of ToBidiagonal subroutine.
+            M           -   number of rows in matrix A.
+            N           -   number of columns in matrix A.
+            TAUQ        -   scalar factors which are used to form Q.
+                            Output of ToBidiagonal subroutine.
+            QColumns    -   required number of columns in matrix Q.
+                            M>=QColumns>=0.
+
+        Output parameters:
+            Q           -   first QColumns columns of matrix Q.
+                            Array[0..M-1, 0..QColumns-1]
+                            If QColumns=0, the array is not modified.
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixbdunpackq(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] tauq,
+            int qcolumns,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+
+            System.Diagnostics.Debug.Assert(qcolumns<=m, "RMatrixBDUnpackQ: QColumns>M!");
+            System.Diagnostics.Debug.Assert(qcolumns>=0, "RMatrixBDUnpackQ: QColumns<0!");
+            if( m==0 | n==0 | qcolumns==0 )
+            {
+                return;
+            }
+            
+            //
+            // prepare Q
+            //
+            q = new double[m-1+1, qcolumns-1+1];
+            for(i=0; i<=m-1; i++)
+            {
+                for(j=0; j<=qcolumns-1; j++)
+                {
+                    if( i==j )
+                    {
+                        q[i,j] = 1;
+                    }
+                    else
+                    {
+                        q[i,j] = 0;
+                    }
+                }
+            }
+            
+            //
+            // Calculate
+            //
+            rmatrixbdmultiplybyq(ref qp, m, n, ref tauq, ref q, m, qcolumns, false, false);
+        }
+
+
+        /*************************************************************************
+        Multiplication by matrix Q which reduces matrix A to  bidiagonal form.
+
+        The algorithm allows pre- or post-multiply by Q or Q'.
+
+        Input parameters:
+            QP          -   matrices Q and P in compact form.
+                            Output of ToBidiagonal subroutine.
+            M           -   number of rows in matrix A.
+            N           -   number of columns in matrix A.
+            TAUQ        -   scalar factors which are used to form Q.
+                            Output of ToBidiagonal subroutine.
+            Z           -   multiplied matrix.
+                            array[0..ZRows-1,0..ZColumns-1]
+            ZRows       -   number of rows in matrix Z. If FromTheRight=False,
+                            ZRows=M, otherwise ZRows can be arbitrary.
+            ZColumns    -   number of columns in matrix Z. If FromTheRight=True,
+                            ZColumns=M, otherwise ZColumns can be arbitrary.
+            FromTheRight -  pre- or post-multiply.
+            DoTranspose -   multiply by Q or Q'.
+
+        Output parameters:
+            Z           -   product of Z and Q.
+                            Array[0..ZRows-1,0..ZColumns-1]
+                            If ZRows=0 or ZColumns=0, the array is not modified.
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixbdmultiplybyq(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] tauq,
+            ref double[,] z,
+            int zrows,
+            int zcolumns,
+            bool fromtheright,
+            bool dotranspose)
+        {
+            int i = 0;
+            int i1 = 0;
+            int i2 = 0;
+            int istep = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int mx = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( m<=0 | n<=0 | zrows<=0 | zcolumns<=0 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(fromtheright & zcolumns==m | !fromtheright & zrows==m, "RMatrixBDMultiplyByQ: incorrect Z size!");
+            
+            //
+            // init
+            //
+            mx = Math.Max(m, n);
+            mx = Math.Max(mx, zrows);
+            mx = Math.Max(mx, zcolumns);
+            v = new double[mx+1];
+            work = new double[mx+1];
+            if( m>=n )
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = 0;
+                    i2 = n-1;
+                    istep = +1;
+                }
+                else
+                {
+                    i1 = n-1;
+                    i2 = 0;
+                    istep = -1;
+                }
+                if( dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                i = i1;
+                do
+                {
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=m-i;i_++)
+                    {
+                        v[i_] = qp[i_+i1_,i];
+                    }
+                    v[1] = 1;
+                    if( fromtheright )
+                    {
+                        reflections.applyreflectionfromtheright(ref z, tauq[i], ref v, 0, zrows-1, i, m-1, ref work);
+                    }
+                    else
+                    {
+                        reflections.applyreflectionfromtheleft(ref z, tauq[i], ref v, i, m-1, 0, zcolumns-1, ref work);
+                    }
+                    i = i+istep;
+                }
+                while( i!=i2+istep );
+            }
+            else
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = 0;
+                    i2 = m-2;
+                    istep = +1;
+                }
+                else
+                {
+                    i1 = m-2;
+                    i2 = 0;
+                    istep = -1;
+                }
+                if( dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                if( m-1>0 )
+                {
+                    i = i1;
+                    do
+                    {
+                        i1_ = (i+1) - (1);
+                        for(i_=1; i_<=m-i-1;i_++)
+                        {
+                            v[i_] = qp[i_+i1_,i];
+                        }
+                        v[1] = 1;
+                        if( fromtheright )
+                        {
+                            reflections.applyreflectionfromtheright(ref z, tauq[i], ref v, 0, zrows-1, i+1, m-1, ref work);
+                        }
+                        else
+                        {
+                            reflections.applyreflectionfromtheleft(ref z, tauq[i], ref v, i+1, m-1, 0, zcolumns-1, ref work);
+                        }
+                        i = i+istep;
+                    }
+                    while( i!=i2+istep );
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Unpacking matrix P which reduces matrix A to bidiagonal form.
+        The subroutine returns transposed matrix P.
+
+        Input parameters:
+            QP      -   matrices Q and P in compact form.
+                        Output of ToBidiagonal subroutine.
+            M       -   number of rows in matrix A.
+            N       -   number of columns in matrix A.
+            TAUP    -   scalar factors which are used to form P.
+                        Output of ToBidiagonal subroutine.
+            PTRows  -   required number of rows of matrix P^T. N >= PTRows >= 0.
+
+        Output parameters:
+            PT      -   first PTRows columns of matrix P^T
+                        Array[0..PTRows-1, 0..N-1]
+                        If PTRows=0, the array is not modified.
+
+          -- ALGLIB --
+             Copyright 2005-2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixbdunpackpt(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] taup,
+            int ptrows,
+            ref double[,] pt)
+        {
+            int i = 0;
+            int j = 0;
+
+            System.Diagnostics.Debug.Assert(ptrows<=n, "RMatrixBDUnpackPT: PTRows>N!");
+            System.Diagnostics.Debug.Assert(ptrows>=0, "RMatrixBDUnpackPT: PTRows<0!");
+            if( m==0 | n==0 | ptrows==0 )
+            {
+                return;
+            }
+            
+            //
+            // prepare PT
+            //
+            pt = new double[ptrows-1+1, n-1+1];
+            for(i=0; i<=ptrows-1; i++)
+            {
+                for(j=0; j<=n-1; j++)
+                {
+                    if( i==j )
+                    {
+                        pt[i,j] = 1;
+                    }
+                    else
+                    {
+                        pt[i,j] = 0;
+                    }
+                }
+            }
+            
+            //
+            // Calculate
+            //
+            rmatrixbdmultiplybyp(ref qp, m, n, ref taup, ref pt, ptrows, n, true, true);
+        }
+
+
+        /*************************************************************************
+        Multiplication by matrix P which reduces matrix A to  bidiagonal form.
+
+        The algorithm allows pre- or post-multiply by P or P'.
+
+        Input parameters:
+            QP          -   matrices Q and P in compact form.
+                            Output of RMatrixBD subroutine.
+            M           -   number of rows in matrix A.
+            N           -   number of columns in matrix A.
+            TAUP        -   scalar factors which are used to form P.
+                            Output of RMatrixBD subroutine.
+            Z           -   multiplied matrix.
+                            Array whose indexes range within [0..ZRows-1,0..ZColumns-1].
+            ZRows       -   number of rows in matrix Z. If FromTheRight=False,
+                            ZRows=N, otherwise ZRows can be arbitrary.
+            ZColumns    -   number of columns in matrix Z. If FromTheRight=True,
+                            ZColumns=N, otherwise ZColumns can be arbitrary.
+            FromTheRight -  pre- or post-multiply.
+            DoTranspose -   multiply by P or P'.
+
+        Output parameters:
+            Z - product of Z and P.
+                        Array whose indexes range within [0..ZRows-1,0..ZColumns-1].
+                        If ZRows=0 or ZColumns=0, the array is not modified.
+
+          -- ALGLIB --
+             Copyright 2005-2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixbdmultiplybyp(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] taup,
+            ref double[,] z,
+            int zrows,
+            int zcolumns,
+            bool fromtheright,
+            bool dotranspose)
+        {
+            int i = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int mx = 0;
+            int i1 = 0;
+            int i2 = 0;
+            int istep = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( m<=0 | n<=0 | zrows<=0 | zcolumns<=0 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(fromtheright & zcolumns==n | !fromtheright & zrows==n, "RMatrixBDMultiplyByP: incorrect Z size!");
+            
+            //
+            // init
+            //
+            mx = Math.Max(m, n);
+            mx = Math.Max(mx, zrows);
+            mx = Math.Max(mx, zcolumns);
+            v = new double[mx+1];
+            work = new double[mx+1];
+            v = new double[mx+1];
+            work = new double[mx+1];
+            if( m>=n )
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = n-2;
+                    i2 = 0;
+                    istep = -1;
+                }
+                else
+                {
+                    i1 = 0;
+                    i2 = n-2;
+                    istep = +1;
+                }
+                if( !dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                if( n-1>0 )
+                {
+                    i = i1;
+                    do
+                    {
+                        i1_ = (i+1) - (1);
+                        for(i_=1; i_<=n-1-i;i_++)
+                        {
+                            v[i_] = qp[i,i_+i1_];
+                        }
+                        v[1] = 1;
+                        if( fromtheright )
+                        {
+                            reflections.applyreflectionfromtheright(ref z, taup[i], ref v, 0, zrows-1, i+1, n-1, ref work);
+                        }
+                        else
+                        {
+                            reflections.applyreflectionfromtheleft(ref z, taup[i], ref v, i+1, n-1, 0, zcolumns-1, ref work);
+                        }
+                        i = i+istep;
+                    }
+                    while( i!=i2+istep );
+                }
+            }
+            else
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = m-1;
+                    i2 = 0;
+                    istep = -1;
+                }
+                else
+                {
+                    i1 = 0;
+                    i2 = m-1;
+                    istep = +1;
+                }
+                if( !dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                i = i1;
+                do
+                {
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=n-i;i_++)
+                    {
+                        v[i_] = qp[i,i_+i1_];
+                    }
+                    v[1] = 1;
+                    if( fromtheright )
+                    {
+                        reflections.applyreflectionfromtheright(ref z, taup[i], ref v, 0, zrows-1, i, n-1, ref work);
+                    }
+                    else
+                    {
+                        reflections.applyreflectionfromtheleft(ref z, taup[i], ref v, i, n-1, 0, zcolumns-1, ref work);
+                    }
+                    i = i+istep;
+                }
+                while( i!=i2+istep );
+            }
+        }
+
+
+        /*************************************************************************
+        Unpacking of the main and secondary diagonals of bidiagonal decomposition
+        of matrix A.
+
+        Input parameters:
+            B   -   output of RMatrixBD subroutine.
+            M   -   number of rows in matrix B.
+            N   -   number of columns in matrix B.
+
+        Output parameters:
+            IsUpper -   True, if the matrix is upper bidiagonal.
+                        otherwise IsUpper is False.
+            D       -   the main diagonal.
+                        Array whose index ranges within [0..Min(M,N)-1].
+            E       -   the secondary diagonal (upper or lower, depending on
+                        the value of IsUpper).
+                        Array index ranges within [0..Min(M,N)-1], the last
+                        element is not used.
+
+          -- ALGLIB --
+             Copyright 2005-2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixbdunpackdiagonals(ref double[,] b,
+            int m,
+            int n,
+            ref bool isupper,
+            ref double[] d,
+            ref double[] e)
+        {
+            int i = 0;
+
+            isupper = m>=n;
+            if( m<=0 | n<=0 )
+            {
+                return;
+            }
+            if( isupper )
+            {
+                d = new double[n-1+1];
+                e = new double[n-1+1];
+                for(i=0; i<=n-2; i++)
+                {
+                    d[i] = b[i,i];
+                    e[i] = b[i,i+1];
+                }
+                d[n-1] = b[n-1,n-1];
+            }
+            else
+            {
+                d = new double[m-1+1];
+                e = new double[m-1+1];
+                for(i=0; i<=m-2; i++)
+                {
+                    d[i] = b[i,i];
+                    e[i] = b[i+1,i];
+                }
+                d[m-1] = b[m-1,m-1];
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixBD for 0-based replacement.
+        *************************************************************************/
+        public static void tobidiagonal(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tauq,
+            ref double[] taup)
+        {
+            double[] work = new double[0];
+            double[] t = new double[0];
+            int minmn = 0;
+            int maxmn = 0;
+            int i = 0;
+            double ltau = 0;
+            int mmip1 = 0;
+            int nmi = 0;
+            int ip1 = 0;
+            int nmip1 = 0;
+            int mmi = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            minmn = Math.Min(m, n);
+            maxmn = Math.Max(m, n);
+            work = new double[maxmn+1];
+            t = new double[maxmn+1];
+            taup = new double[minmn+1];
+            tauq = new double[minmn+1];
+            if( m>=n )
+            {
+                
+                //
+                // Reduce to upper bidiagonal form
+                //
+                for(i=1; i<=n; i++)
+                {
+                    
+                    //
+                    // Generate elementary reflector H(i) to annihilate A(i+1:m,i)
+                    //
+                    mmip1 = m-i+1;
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=mmip1;i_++)
+                    {
+                        t[i_] = a[i_+i1_,i];
+                    }
+                    reflections.generatereflection(ref t, mmip1, ref ltau);
+                    tauq[i] = ltau;
+                    i1_ = (1) - (i);
+                    for(i_=i; i_<=m;i_++)
+                    {
+                        a[i_,i] = t[i_+i1_];
+                    }
+                    t[1] = 1;
+                    
+                    //
+                    // Apply H(i) to A(i:m,i+1:n) from the left
+                    //
+                    reflections.applyreflectionfromtheleft(ref a, ltau, ref t, i, m, i+1, n, ref work);
+                    if( i<n )
+                    {
+                        
+                        //
+                        // Generate elementary reflector G(i) to annihilate
+                        // A(i,i+2:n)
+                        //
+                        nmi = n-i;
+                        ip1 = i+1;
+                        i1_ = (ip1) - (1);
+                        for(i_=1; i_<=nmi;i_++)
+                        {
+                            t[i_] = a[i,i_+i1_];
+                        }
+                        reflections.generatereflection(ref t, nmi, ref ltau);
+                        taup[i] = ltau;
+                        i1_ = (1) - (ip1);
+                        for(i_=ip1; i_<=n;i_++)
+                        {
+                            a[i,i_] = t[i_+i1_];
+                        }
+                        t[1] = 1;
+                        
+                        //
+                        // Apply G(i) to A(i+1:m,i+1:n) from the right
+                        //
+                        reflections.applyreflectionfromtheright(ref a, ltau, ref t, i+1, m, i+1, n, ref work);
+                    }
+                    else
+                    {
+                        taup[i] = 0;
+                    }
+                }
+            }
+            else
+            {
+                
+                //
+                // Reduce to lower bidiagonal form
+                //
+                for(i=1; i<=m; i++)
+                {
+                    
+                    //
+                    // Generate elementary reflector G(i) to annihilate A(i,i+1:n)
+                    //
+                    nmip1 = n-i+1;
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=nmip1;i_++)
+                    {
+                        t[i_] = a[i,i_+i1_];
+                    }
+                    reflections.generatereflection(ref t, nmip1, ref ltau);
+                    taup[i] = ltau;
+                    i1_ = (1) - (i);
+                    for(i_=i; i_<=n;i_++)
+                    {
+                        a[i,i_] = t[i_+i1_];
+                    }
+                    t[1] = 1;
+                    
+                    //
+                    // Apply G(i) to A(i+1:m,i:n) from the right
+                    //
+                    reflections.applyreflectionfromtheright(ref a, ltau, ref t, i+1, m, i, n, ref work);
+                    if( i<m )
+                    {
+                        
+                        //
+                        // Generate elementary reflector H(i) to annihilate
+                        // A(i+2:m,i)
+                        //
+                        mmi = m-i;
+                        ip1 = i+1;
+                        i1_ = (ip1) - (1);
+                        for(i_=1; i_<=mmi;i_++)
+                        {
+                            t[i_] = a[i_+i1_,i];
+                        }
+                        reflections.generatereflection(ref t, mmi, ref ltau);
+                        tauq[i] = ltau;
+                        i1_ = (1) - (ip1);
+                        for(i_=ip1; i_<=m;i_++)
+                        {
+                            a[i_,i] = t[i_+i1_];
+                        }
+                        t[1] = 1;
+                        
+                        //
+                        // Apply H(i) to A(i+1:m,i+1:n) from the left
+                        //
+                        reflections.applyreflectionfromtheleft(ref a, ltau, ref t, i+1, m, i+1, n, ref work);
+                    }
+                    else
+                    {
+                        tauq[i] = 0;
+                    }
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixBDUnpackQ for 0-based replacement.
+        *************************************************************************/
+        public static void unpackqfrombidiagonal(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] tauq,
+            int qcolumns,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+            int ip1 = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int vm = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            System.Diagnostics.Debug.Assert(qcolumns<=m, "UnpackQFromBidiagonal: QColumns>M!");
+            if( m==0 | n==0 | qcolumns==0 )
+            {
+                return;
+            }
+            
+            //
+            // init
+            //
+            q = new double[m+1, qcolumns+1];
+            v = new double[m+1];
+            work = new double[qcolumns+1];
+            
+            //
+            // prepare Q
+            //
+            for(i=1; i<=m; i++)
+            {
+                for(j=1; j<=qcolumns; j++)
+                {
+                    if( i==j )
+                    {
+                        q[i,j] = 1;
+                    }
+                    else
+                    {
+                        q[i,j] = 0;
+                    }
+                }
+            }
+            if( m>=n )
+            {
+                for(i=Math.Min(n, qcolumns); i>=1; i--)
+                {
+                    vm = m-i+1;
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=vm;i_++)
+                    {
+                        v[i_] = qp[i_+i1_,i];
+                    }
+                    v[1] = 1;
+                    reflections.applyreflectionfromtheleft(ref q, tauq[i], ref v, i, m, 1, qcolumns, ref work);
+                }
+            }
+            else
+            {
+                for(i=Math.Min(m-1, qcolumns-1); i>=1; i--)
+                {
+                    vm = m-i;
+                    ip1 = i+1;
+                    i1_ = (ip1) - (1);
+                    for(i_=1; i_<=vm;i_++)
+                    {
+                        v[i_] = qp[i_+i1_,i];
+                    }
+                    v[1] = 1;
+                    reflections.applyreflectionfromtheleft(ref q, tauq[i], ref v, i+1, m, 1, qcolumns, ref work);
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixBDMultiplyByQ for 0-based replacement.
+        *************************************************************************/
+        public static void multiplybyqfrombidiagonal(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] tauq,
+            ref double[,] z,
+            int zrows,
+            int zcolumns,
+            bool fromtheright,
+            bool dotranspose)
+        {
+            int i = 0;
+            int ip1 = 0;
+            int i1 = 0;
+            int i2 = 0;
+            int istep = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int vm = 0;
+            int mx = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( m<=0 | n<=0 | zrows<=0 | zcolumns<=0 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(fromtheright & zcolumns==m | !fromtheright & zrows==m, "MultiplyByQFromBidiagonal: incorrect Z size!");
+            
+            //
+            // init
+            //
+            mx = Math.Max(m, n);
+            mx = Math.Max(mx, zrows);
+            mx = Math.Max(mx, zcolumns);
+            v = new double[mx+1];
+            work = new double[mx+1];
+            if( m>=n )
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = 1;
+                    i2 = n;
+                    istep = +1;
+                }
+                else
+                {
+                    i1 = n;
+                    i2 = 1;
+                    istep = -1;
+                }
+                if( dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                i = i1;
+                do
+                {
+                    vm = m-i+1;
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=vm;i_++)
+                    {
+                        v[i_] = qp[i_+i1_,i];
+                    }
+                    v[1] = 1;
+                    if( fromtheright )
+                    {
+                        reflections.applyreflectionfromtheright(ref z, tauq[i], ref v, 1, zrows, i, m, ref work);
+                    }
+                    else
+                    {
+                        reflections.applyreflectionfromtheleft(ref z, tauq[i], ref v, i, m, 1, zcolumns, ref work);
+                    }
+                    i = i+istep;
+                }
+                while( i!=i2+istep );
+            }
+            else
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = 1;
+                    i2 = m-1;
+                    istep = +1;
+                }
+                else
+                {
+                    i1 = m-1;
+                    i2 = 1;
+                    istep = -1;
+                }
+                if( dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                if( m-1>0 )
+                {
+                    i = i1;
+                    do
+                    {
+                        vm = m-i;
+                        ip1 = i+1;
+                        i1_ = (ip1) - (1);
+                        for(i_=1; i_<=vm;i_++)
+                        {
+                            v[i_] = qp[i_+i1_,i];
+                        }
+                        v[1] = 1;
+                        if( fromtheright )
+                        {
+                            reflections.applyreflectionfromtheright(ref z, tauq[i], ref v, 1, zrows, i+1, m, ref work);
+                        }
+                        else
+                        {
+                            reflections.applyreflectionfromtheleft(ref z, tauq[i], ref v, i+1, m, 1, zcolumns, ref work);
+                        }
+                        i = i+istep;
+                    }
+                    while( i!=i2+istep );
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixBDUnpackPT for 0-based replacement.
+        *************************************************************************/
+        public static void unpackptfrombidiagonal(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] taup,
+            int ptrows,
+            ref double[,] pt)
+        {
+            int i = 0;
+            int j = 0;
+            int ip1 = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int vm = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            System.Diagnostics.Debug.Assert(ptrows<=n, "UnpackPTFromBidiagonal: PTRows>N!");
+            if( m==0 | n==0 | ptrows==0 )
+            {
+                return;
+            }
+            
+            //
+            // init
+            //
+            pt = new double[ptrows+1, n+1];
+            v = new double[n+1];
+            work = new double[ptrows+1];
+            
+            //
+            // prepare PT
+            //
+            for(i=1; i<=ptrows; i++)
+            {
+                for(j=1; j<=n; j++)
+                {
+                    if( i==j )
+                    {
+                        pt[i,j] = 1;
+                    }
+                    else
+                    {
+                        pt[i,j] = 0;
+                    }
+                }
+            }
+            if( m>=n )
+            {
+                for(i=Math.Min(n-1, ptrows-1); i>=1; i--)
+                {
+                    vm = n-i;
+                    ip1 = i+1;
+                    i1_ = (ip1) - (1);
+                    for(i_=1; i_<=vm;i_++)
+                    {
+                        v[i_] = qp[i,i_+i1_];
+                    }
+                    v[1] = 1;
+                    reflections.applyreflectionfromtheright(ref pt, taup[i], ref v, 1, ptrows, i+1, n, ref work);
+                }
+            }
+            else
+            {
+                for(i=Math.Min(m, ptrows); i>=1; i--)
+                {
+                    vm = n-i+1;
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=vm;i_++)
+                    {
+                        v[i_] = qp[i,i_+i1_];
+                    }
+                    v[1] = 1;
+                    reflections.applyreflectionfromtheright(ref pt, taup[i], ref v, 1, ptrows, i, n, ref work);
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixBDMultiplyByP for 0-based replacement.
+        *************************************************************************/
+        public static void multiplybypfrombidiagonal(ref double[,] qp,
+            int m,
+            int n,
+            ref double[] taup,
+            ref double[,] z,
+            int zrows,
+            int zcolumns,
+            bool fromtheright,
+            bool dotranspose)
+        {
+            int i = 0;
+            int ip1 = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int vm = 0;
+            int mx = 0;
+            int i1 = 0;
+            int i2 = 0;
+            int istep = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( m<=0 | n<=0 | zrows<=0 | zcolumns<=0 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(fromtheright & zcolumns==n | !fromtheright & zrows==n, "MultiplyByQFromBidiagonal: incorrect Z size!");
+            
+            //
+            // init
+            //
+            mx = Math.Max(m, n);
+            mx = Math.Max(mx, zrows);
+            mx = Math.Max(mx, zcolumns);
+            v = new double[mx+1];
+            work = new double[mx+1];
+            v = new double[mx+1];
+            work = new double[mx+1];
+            if( m>=n )
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = n-1;
+                    i2 = 1;
+                    istep = -1;
+                }
+                else
+                {
+                    i1 = 1;
+                    i2 = n-1;
+                    istep = +1;
+                }
+                if( !dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                if( n-1>0 )
+                {
+                    i = i1;
+                    do
+                    {
+                        vm = n-i;
+                        ip1 = i+1;
+                        i1_ = (ip1) - (1);
+                        for(i_=1; i_<=vm;i_++)
+                        {
+                            v[i_] = qp[i,i_+i1_];
+                        }
+                        v[1] = 1;
+                        if( fromtheright )
+                        {
+                            reflections.applyreflectionfromtheright(ref z, taup[i], ref v, 1, zrows, i+1, n, ref work);
+                        }
+                        else
+                        {
+                            reflections.applyreflectionfromtheleft(ref z, taup[i], ref v, i+1, n, 1, zcolumns, ref work);
+                        }
+                        i = i+istep;
+                    }
+                    while( i!=i2+istep );
+                }
+            }
+            else
+            {
+                
+                //
+                // setup
+                //
+                if( fromtheright )
+                {
+                    i1 = m;
+                    i2 = 1;
+                    istep = -1;
+                }
+                else
+                {
+                    i1 = 1;
+                    i2 = m;
+                    istep = +1;
+                }
+                if( !dotranspose )
+                {
+                    i = i1;
+                    i1 = i2;
+                    i2 = i;
+                    istep = -istep;
+                }
+                
+                //
+                // Process
+                //
+                i = i1;
+                do
+                {
+                    vm = n-i+1;
+                    i1_ = (i) - (1);
+                    for(i_=1; i_<=vm;i_++)
+                    {
+                        v[i_] = qp[i,i_+i1_];
+                    }
+                    v[1] = 1;
+                    if( fromtheright )
+                    {
+                        reflections.applyreflectionfromtheright(ref z, taup[i], ref v, 1, zrows, i, n, ref work);
+                    }
+                    else
+                    {
+                        reflections.applyreflectionfromtheleft(ref z, taup[i], ref v, i, n, 1, zcolumns, ref work);
+                    }
+                    i = i+istep;
+                }
+                while( i!=i2+istep );
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixBDUnpackDiagonals for 0-based replacement.
+        *************************************************************************/
+        public static void unpackdiagonalsfrombidiagonal(ref double[,] b,
+            int m,
+            int n,
+            ref bool isupper,
+            ref double[] d,
+            ref double[] e)
+        {
+            int i = 0;
+
+            isupper = m>=n;
+            if( m==0 | n==0 )
+            {
+                return;
+            }
+            if( isupper )
+            {
+                d = new double[n+1];
+                e = new double[n+1];
+                for(i=1; i<=n-1; i++)
+                {
+                    d[i] = b[i,i];
+                    e[i] = b[i,i+1];
+                }
+                d[n] = b[n,n];
+            }
+            else
+            {
+                d = new double[m+1];
+                e = new double[m+1];
+                for(i=1; i<=m-1; i++)
+                {
+                    d[i] = b[i,i];
+                    e[i] = b[i+1,i];
+                }
+                d[m] = b[m,m];
+            }
+        }
+    }
+}
Index: trunk/sources/ALGLIB/blas.cs
===================================================================
--- trunk/sources/ALGLIB/blas.cs	(revision 2430)
+++ trunk/sources/ALGLIB/blas.cs	(revision 2430)
@@ -0,0 +1,660 @@
+/*************************************************************************
+Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project).
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class blas
+    {
+        public static double vectornorm2(ref double[] x,
+            int i1,
+            int i2)
+        {
+            double result = 0;
+            int n = 0;
+            int ix = 0;
+            double absxi = 0;
+            double scl = 0;
+            double ssq = 0;
+
+            n = i2-i1+1;
+            if( n<1 )
+            {
+                result = 0;
+                return result;
+            }
+            if( n==1 )
+            {
+                result = Math.Abs(x[i1]);
+                return result;
+            }
+            scl = 0;
+            ssq = 1;
+            for(ix=i1; ix<=i2; ix++)
+            {
+                if( x[ix]!=0 )
+                {
+                    absxi = Math.Abs(x[ix]);
+                    if( scl<absxi )
+                    {
+                        ssq = 1+ssq*AP.Math.Sqr(scl/absxi);
+                        scl = absxi;
+                    }
+                    else
+                    {
+                        ssq = ssq+AP.Math.Sqr(absxi/scl);
+                    }
+                }
+            }
+            result = scl*Math.Sqrt(ssq);
+            return result;
+        }
+
+
+        public static int vectoridxabsmax(ref double[] x,
+            int i1,
+            int i2)
+        {
+            int result = 0;
+            int i = 0;
+            double a = 0;
+
+            result = i1;
+            a = Math.Abs(x[result]);
+            for(i=i1+1; i<=i2; i++)
+            {
+                if( Math.Abs(x[i])>Math.Abs(x[result]) )
+                {
+                    result = i;
+                }
+            }
+            return result;
+        }
+
+
+        public static int columnidxabsmax(ref double[,] x,
+            int i1,
+            int i2,
+            int j)
+        {
+            int result = 0;
+            int i = 0;
+            double a = 0;
+
+            result = i1;
+            a = Math.Abs(x[result,j]);
+            for(i=i1+1; i<=i2; i++)
+            {
+                if( Math.Abs(x[i,j])>Math.Abs(x[result,j]) )
+                {
+                    result = i;
+                }
+            }
+            return result;
+        }
+
+
+        public static int rowidxabsmax(ref double[,] x,
+            int j1,
+            int j2,
+            int i)
+        {
+            int result = 0;
+            int j = 0;
+            double a = 0;
+
+            result = j1;
+            a = Math.Abs(x[i,result]);
+            for(j=j1+1; j<=j2; j++)
+            {
+                if( Math.Abs(x[i,j])>Math.Abs(x[i,result]) )
+                {
+                    result = j;
+                }
+            }
+            return result;
+        }
+
+
+        public static double upperhessenberg1norm(ref double[,] a,
+            int i1,
+            int i2,
+            int j1,
+            int j2,
+            ref double[] work)
+        {
+            double result = 0;
+            int i = 0;
+            int j = 0;
+
+            System.Diagnostics.Debug.Assert(i2-i1==j2-j1, "UpperHessenberg1Norm: I2-I1<>J2-J1!");
+            for(j=j1; j<=j2; j++)
+            {
+                work[j] = 0;
+            }
+            for(i=i1; i<=i2; i++)
+            {
+                for(j=Math.Max(j1, j1+i-i1-1); j<=j2; j++)
+                {
+                    work[j] = work[j]+Math.Abs(a[i,j]);
+                }
+            }
+            result = 0;
+            for(j=j1; j<=j2; j++)
+            {
+                result = Math.Max(result, work[j]);
+            }
+            return result;
+        }
+
+
+        public static void copymatrix(ref double[,] a,
+            int is1,
+            int is2,
+            int js1,
+            int js2,
+            ref double[,] b,
+            int id1,
+            int id2,
+            int jd1,
+            int jd2)
+        {
+            int isrc = 0;
+            int idst = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( is1>is2 | js1>js2 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(is2-is1==id2-id1, "CopyMatrix: different sizes!");
+            System.Diagnostics.Debug.Assert(js2-js1==jd2-jd1, "CopyMatrix: different sizes!");
+            for(isrc=is1; isrc<=is2; isrc++)
+            {
+                idst = isrc-is1+id1;
+                i1_ = (js1) - (jd1);
+                for(i_=jd1; i_<=jd2;i_++)
+                {
+                    b[idst,i_] = a[isrc,i_+i1_];
+                }
+            }
+        }
+
+
+        public static void inplacetranspose(ref double[,] a,
+            int i1,
+            int i2,
+            int j1,
+            int j2,
+            ref double[] work)
+        {
+            int i = 0;
+            int j = 0;
+            int ips = 0;
+            int jps = 0;
+            int l = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( i1>i2 | j1>j2 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(i1-i2==j1-j2, "InplaceTranspose error: incorrect array size!");
+            for(i=i1; i<=i2-1; i++)
+            {
+                j = j1+i-i1;
+                ips = i+1;
+                jps = j1+ips-i1;
+                l = i2-i;
+                i1_ = (ips) - (1);
+                for(i_=1; i_<=l;i_++)
+                {
+                    work[i_] = a[i_+i1_,j];
+                }
+                i1_ = (jps) - (ips);
+                for(i_=ips; i_<=i2;i_++)
+                {
+                    a[i_,j] = a[i,i_+i1_];
+                }
+                i1_ = (1) - (jps);
+                for(i_=jps; i_<=j2;i_++)
+                {
+                    a[i,i_] = work[i_+i1_];
+                }
+            }
+        }
+
+
+        public static void copyandtranspose(ref double[,] a,
+            int is1,
+            int is2,
+            int js1,
+            int js2,
+            ref double[,] b,
+            int id1,
+            int id2,
+            int jd1,
+            int jd2)
+        {
+            int isrc = 0;
+            int jdst = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( is1>is2 | js1>js2 )
+            {
+                return;
+            }
+            System.Diagnostics.Debug.Assert(is2-is1==jd2-jd1, "CopyAndTranspose: different sizes!");
+            System.Diagnostics.Debug.Assert(js2-js1==id2-id1, "CopyAndTranspose: different sizes!");
+            for(isrc=is1; isrc<=is2; isrc++)
+            {
+                jdst = isrc-is1+jd1;
+                i1_ = (js1) - (id1);
+                for(i_=id1; i_<=id2;i_++)
+                {
+                    b[i_,jdst] = a[isrc,i_+i1_];
+                }
+            }
+        }
+
+
+        public static void matrixvectormultiply(ref double[,] a,
+            int i1,
+            int i2,
+            int j1,
+            int j2,
+            bool trans,
+            ref double[] x,
+            int ix1,
+            int ix2,
+            double alpha,
+            ref double[] y,
+            int iy1,
+            int iy2,
+            double beta)
+        {
+            int i = 0;
+            double v = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( !trans )
+            {
+                
+                //
+                // y := alpha*A*x + beta*y;
+                //
+                if( i1>i2 | j1>j2 )
+                {
+                    return;
+                }
+                System.Diagnostics.Debug.Assert(j2-j1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!");
+                System.Diagnostics.Debug.Assert(i2-i1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!");
+                
+                //
+                // beta*y
+                //
+                if( beta==0 )
+                {
+                    for(i=iy1; i<=iy2; i++)
+                    {
+                        y[i] = 0;
+                    }
+                }
+                else
+                {
+                    for(i_=iy1; i_<=iy2;i_++)
+                    {
+                        y[i_] = beta*y[i_];
+                    }
+                }
+                
+                //
+                // alpha*A*x
+                //
+                for(i=i1; i<=i2; i++)
+                {
+                    i1_ = (ix1)-(j1);
+                    v = 0.0;
+                    for(i_=j1; i_<=j2;i_++)
+                    {
+                        v += a[i,i_]*x[i_+i1_];
+                    }
+                    y[iy1+i-i1] = y[iy1+i-i1]+alpha*v;
+                }
+            }
+            else
+            {
+                
+                //
+                // y := alpha*A'*x + beta*y;
+                //
+                if( i1>i2 | j1>j2 )
+                {
+                    return;
+                }
+                System.Diagnostics.Debug.Assert(i2-i1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!");
+                System.Diagnostics.Debug.Assert(j2-j1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!");
+                
+                //
+                // beta*y
+                //
+                if( beta==0 )
+                {
+                    for(i=iy1; i<=iy2; i++)
+                    {
+                        y[i] = 0;
+                    }
+                }
+                else
+                {
+                    for(i_=iy1; i_<=iy2;i_++)
+                    {
+                        y[i_] = beta*y[i_];
+                    }
+                }
+                
+                //
+                // alpha*A'*x
+                //
+                for(i=i1; i<=i2; i++)
+                {
+                    v = alpha*x[ix1+i-i1];
+                    i1_ = (j1) - (iy1);
+                    for(i_=iy1; i_<=iy2;i_++)
+                    {
+                        y[i_] = y[i_] + v*a[i,i_+i1_];
+                    }
+                }
+            }
+        }
+
+
+        public static double pythag2(double x,
+            double y)
+        {
+            double result = 0;
+            double w = 0;
+            double xabs = 0;
+            double yabs = 0;
+            double z = 0;
+
+            xabs = Math.Abs(x);
+            yabs = Math.Abs(y);
+            w = Math.Max(xabs, yabs);
+            z = Math.Min(xabs, yabs);
+            if( z==0 )
+            {
+                result = w;
+            }
+            else
+            {
+                result = w*Math.Sqrt(1+AP.Math.Sqr(z/w));
+            }
+            return result;
+        }
+
+
+        public static void matrixmatrixmultiply(ref double[,] a,
+            int ai1,
+            int ai2,
+            int aj1,
+            int aj2,
+            bool transa,
+            ref double[,] b,
+            int bi1,
+            int bi2,
+            int bj1,
+            int bj2,
+            bool transb,
+            double alpha,
+            ref double[,] c,
+            int ci1,
+            int ci2,
+            int cj1,
+            int cj2,
+            double beta,
+            ref double[] work)
+        {
+            int arows = 0;
+            int acols = 0;
+            int brows = 0;
+            int bcols = 0;
+            int crows = 0;
+            int ccols = 0;
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int l = 0;
+            int r = 0;
+            double v = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            
+            //
+            // Setup
+            //
+            if( !transa )
+            {
+                arows = ai2-ai1+1;
+                acols = aj2-aj1+1;
+            }
+            else
+            {
+                arows = aj2-aj1+1;
+                acols = ai2-ai1+1;
+            }
+            if( !transb )
+            {
+                brows = bi2-bi1+1;
+                bcols = bj2-bj1+1;
+            }
+            else
+            {
+                brows = bj2-bj1+1;
+                bcols = bi2-bi1+1;
+            }
+            System.Diagnostics.Debug.Assert(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!");
+            if( arows<=0 | acols<=0 | brows<=0 | bcols<=0 )
+            {
+                return;
+            }
+            crows = arows;
+            ccols = bcols;
+            
+            //
+            // Test WORK
+            //
+            i = Math.Max(arows, acols);
+            i = Math.Max(brows, i);
+            i = Math.Max(i, bcols);
+            work[1] = 0;
+            work[i] = 0;
+            
+            //
+            // Prepare C
+            //
+            if( beta==0 )
+            {
+                for(i=ci1; i<=ci2; i++)
+                {
+                    for(j=cj1; j<=cj2; j++)
+                    {
+                        c[i,j] = 0;
+                    }
+                }
+            }
+            else
+            {
+                for(i=ci1; i<=ci2; i++)
+                {
+                    for(i_=cj1; i_<=cj2;i_++)
+                    {
+                        c[i,i_] = beta*c[i,i_];
+                    }
+                }
+            }
+            
+            //
+            // A*B
+            //
+            if( !transa & !transb )
+            {
+                for(l=ai1; l<=ai2; l++)
+                {
+                    for(r=bi1; r<=bi2; r++)
+                    {
+                        v = alpha*a[l,aj1+r-bi1];
+                        k = ci1+l-ai1;
+                        i1_ = (bj1) - (cj1);
+                        for(i_=cj1; i_<=cj2;i_++)
+                        {
+                            c[k,i_] = c[k,i_] + v*b[r,i_+i1_];
+                        }
+                    }
+                }
+                return;
+            }
+            
+            //
+            // A*B'
+            //
+            if( !transa & transb )
+            {
+                if( arows*acols<brows*bcols )
+                {
+                    for(r=bi1; r<=bi2; r++)
+                    {
+                        for(l=ai1; l<=ai2; l++)
+                        {
+                            i1_ = (bj1)-(aj1);
+                            v = 0.0;
+                            for(i_=aj1; i_<=aj2;i_++)
+                            {
+                                v += a[l,i_]*b[r,i_+i1_];
+                            }
+                            c[ci1+l-ai1,cj1+r-bi1] = c[ci1+l-ai1,cj1+r-bi1]+alpha*v;
+                        }
+                    }
+                    return;
+                }
+                else
+                {
+                    for(l=ai1; l<=ai2; l++)
+                    {
+                        for(r=bi1; r<=bi2; r++)
+                        {
+                            i1_ = (bj1)-(aj1);
+                            v = 0.0;
+                            for(i_=aj1; i_<=aj2;i_++)
+                            {
+                                v += a[l,i_]*b[r,i_+i1_];
+                            }
+                            c[ci1+l-ai1,cj1+r-bi1] = c[ci1+l-ai1,cj1+r-bi1]+alpha*v;
+                        }
+                    }
+                    return;
+                }
+            }
+            
+            //
+            // A'*B
+            //
+            if( transa & !transb )
+            {
+                for(l=aj1; l<=aj2; l++)
+                {
+                    for(r=bi1; r<=bi2; r++)
+                    {
+                        v = alpha*a[ai1+r-bi1,l];
+                        k = ci1+l-aj1;
+                        i1_ = (bj1) - (cj1);
+                        for(i_=cj1; i_<=cj2;i_++)
+                        {
+                            c[k,i_] = c[k,i_] + v*b[r,i_+i1_];
+                        }
+                    }
+                }
+                return;
+            }
+            
+            //
+            // A'*B'
+            //
+            if( transa & transb )
+            {
+                if( arows*acols<brows*bcols )
+                {
+                    for(r=bi1; r<=bi2; r++)
+                    {
+                        for(i=1; i<=crows; i++)
+                        {
+                            work[i] = 0.0;
+                        }
+                        for(l=ai1; l<=ai2; l++)
+                        {
+                            v = alpha*b[r,bj1+l-ai1];
+                            k = cj1+r-bi1;
+                            i1_ = (aj1) - (1);
+                            for(i_=1; i_<=crows;i_++)
+                            {
+                                work[i_] = work[i_] + v*a[l,i_+i1_];
+                            }
+                        }
+                        i1_ = (1) - (ci1);
+                        for(i_=ci1; i_<=ci2;i_++)
+                        {
+                            c[i_,k] = c[i_,k] + work[i_+i1_];
+                        }
+                    }
+                    return;
+                }
+                else
+                {
+                    for(l=aj1; l<=aj2; l++)
+                    {
+                        k = ai2-ai1+1;
+                        i1_ = (ai1) - (1);
+                        for(i_=1; i_<=k;i_++)
+                        {
+                            work[i_] = a[i_+i1_,l];
+                        }
+                        for(r=bi1; r<=bi2; r++)
+                        {
+                            i1_ = (bj1)-(1);
+                            v = 0.0;
+                            for(i_=1; i_<=k;i_++)
+                            {
+                                v += work[i_]*b[r,i_+i1_];
+                            }
+                            c[ci1+l-aj1,cj1+r-bi1] = c[ci1+l-aj1,cj1+r-bi1]+alpha*v;
+                        }
+                    }
+                    return;
+                }
+            }
+        }
+    }
+}
Index: trunk/sources/ALGLIB/correlation.cs
===================================================================
--- trunk/sources/ALGLIB/correlation.cs	(revision 2430)
+++ trunk/sources/ALGLIB/correlation.cs	(revision 2430)
@@ -0,0 +1,272 @@
+/*************************************************************************
+Copyright (c) 2007, Sergey Bochkanov (ALGLIB project).
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class correlation
+    {
+        /*************************************************************************
+        Pearson product-moment correlation coefficient
+
+        Input parameters:
+            X       -   sample 1 (array indexes: [0..N-1])
+            Y       -   sample 2 (array indexes: [0..N-1])
+            N       -   sample size.
+
+        Result:
+            Pearson product-moment correlation coefficient
+
+          -- ALGLIB --
+             Copyright 09.04.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static double pearsoncorrelation(ref double[] x,
+            ref double[] y,
+            int n)
+        {
+            double result = 0;
+            int i = 0;
+            double xmean = 0;
+            double ymean = 0;
+            double s = 0;
+            double xv = 0;
+            double yv = 0;
+            double t1 = 0;
+            double t2 = 0;
+
+            xv = 0;
+            yv = 0;
+            if( n<=1 )
+            {
+                result = 0;
+                return result;
+            }
+            
+            //
+            // Mean
+            //
+            xmean = 0;
+            ymean = 0;
+            for(i=0; i<=n-1; i++)
+            {
+                xmean = xmean+x[i];
+                ymean = ymean+y[i];
+            }
+            xmean = xmean/n;
+            ymean = ymean/n;
+            
+            //
+            // numerator and denominator
+            //
+            s = 0;
+            xv = 0;
+            yv = 0;
+            for(i=0; i<=n-1; i++)
+            {
+                t1 = x[i]-xmean;
+                t2 = y[i]-ymean;
+                xv = xv+AP.Math.Sqr(t1);
+                yv = yv+AP.Math.Sqr(t2);
+                s = s+t1*t2;
+            }
+            if( xv==0 | yv==0 )
+            {
+                result = 0;
+            }
+            else
+            {
+                result = s/(Math.Sqrt(xv)*Math.Sqrt(yv));
+            }
+            return result;
+        }
+
+
+        /*************************************************************************
+        Spearman's rank correlation coefficient
+
+        Input parameters:
+            X       -   sample 1 (array indexes: [0..N-1])
+            Y       -   sample 2 (array indexes: [0..N-1])
+            N       -   sample size.
+
+        Result:
+            Spearman's rank correlation coefficient
+
+          -- ALGLIB --
+             Copyright 09.04.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static double spearmanrankcorrelation(double[] x,
+            double[] y,
+            int n)
+        {
+            double result = 0;
+
+            x = (double[])x.Clone();
+            y = (double[])y.Clone();
+
+            rankx(ref x, n);
+            rankx(ref y, n);
+            result = pearsoncorrelation(ref x, ref y, n);
+            return result;
+        }
+
+
+        /*************************************************************************
+        Internal ranking subroutine
+        *************************************************************************/
+        private static void rankx(ref double[] x,
+            int n)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int t = 0;
+            double tmp = 0;
+            int tmpi = 0;
+            double[] r = new double[0];
+            int[] c = new int[0];
+
+            
+            //
+            // Prepare
+            //
+            if( n<=1 )
+            {
+                x[0] = 1;
+                return;
+            }
+            r = new double[n-1+1];
+            c = new int[n-1+1];
+            for(i=0; i<=n-1; i++)
+            {
+                r[i] = x[i];
+                c[i] = i;
+            }
+            
+            //
+            // sort {R, C}
+            //
+            if( n!=1 )
+            {
+                i = 2;
+                do
+                {
+                    t = i;
+                    while( t!=1 )
+                    {
+                        k = t/2;
+                        if( r[k-1]>=r[t-1] )
+                        {
+                            t = 1;
+                        }
+                        else
+                        {
+                            tmp = r[k-1];
+                            r[k-1] = r[t-1];
+                            r[t-1] = tmp;
+                            tmpi = c[k-1];
+                            c[k-1] = c[t-1];
+                            c[t-1] = tmpi;
+                            t = k;
+                        }
+                    }
+                    i = i+1;
+                }
+                while( i<=n );
+                i = n-1;
+                do
+                {
+                    tmp = r[i];
+                    r[i] = r[0];
+                    r[0] = tmp;
+                    tmpi = c[i];
+                    c[i] = c[0];
+                    c[0] = tmpi;
+                    t = 1;
+                    while( t!=0 )
+                    {
+                        k = 2*t;
+                        if( k>i )
+                        {
+                            t = 0;
+                        }
+                        else
+                        {
+                            if( k<i )
+                            {
+                                if( r[k]>r[k-1] )
+                                {
+                                    k = k+1;
+                                }
+                            }
+                            if( r[t-1]>=r[k-1] )
+                            {
+                                t = 0;
+                            }
+                            else
+                            {
+                                tmp = r[k-1];
+                                r[k-1] = r[t-1];
+                                r[t-1] = tmp;
+                                tmpi = c[k-1];
+                                c[k-1] = c[t-1];
+                                c[t-1] = tmpi;
+                                t = k;
+                            }
+                        }
+                    }
+                    i = i-1;
+                }
+                while( i>=1 );
+            }
+            
+            //
+            // compute tied ranks
+            //
+            i = 0;
+            while( i<=n-1 )
+            {
+                j = i+1;
+                while( j<=n-1 )
+                {
+                    if( r[j]!=r[i] )
+                    {
+                        break;
+                    }
+                    j = j+1;
+                }
+                for(k=i; k<=j-1; k++)
+                {
+                    r[k] = 1+((double)(i+j-1))/(double)(2);
+                }
+                i = j;
+            }
+            
+            //
+            // back to x
+            //
+            for(i=0; i<=n-1; i++)
+            {
+                x[c[i]] = r[i];
+            }
+        }
+    }
+}
Index: trunk/sources/ALGLIB/faq.english.html
===================================================================
--- trunk/sources/ALGLIB/faq.english.html	(revision 2430)
+++ trunk/sources/ALGLIB/faq.english.html	(revision 2430)
@@ -0,0 +1,258 @@
+<html>
+<head>
+
+<title>ALGLIB FAQ</title>
+<style type="text/css">
+<!--
+h1 { font-family: Tahoma,sans-serif; font-size : larger; }
+h2 { font-family: Arial,sans-serif; font-size : 11pt; }
+h3 { font-family: Arial,sans-serif; font-size : 9pt; }
+.cond  { color:blue; }
+.const { color:#222222; }
+.func  { color:#111111; }
+.smalltext { font-size : 10pt; }
+-->
+</style>
+</head>
+<body>
+
+<p align=justify>
+<b>C++</b><br>
+<span class=smalltext>
+<a href="#stdafx">What is the blank file <code>stdafx.h</code> here for?</a><br>
+<a href="#cpparrays">How is the array operations organized in C++?</a><br>
+<a href="#cpparrays2">Why are round brackets used when addressing arrays, not square brackets?</a><br>
+<a href="#cpp64">Will the ALGLIB package work in 64-bit environment?</a><br>
+</span><br>
+
+<b>Delphi</b><br>
+
+<span class=smalltext>
+<a href="#forwhile">Why is <i>while</i> cycle used instead of the <i>for</i> cycle?</a><br>
+<a href="#dac">What is the purpose of the <code>DynamicArrayCopy</code> function?</a><br>
+</span><br>
+
+<b>Visual Basic</b><br>
+
+<span class=smalltext>
+<a href="#vbver">What version of Visual Basic are the algorithms translated into?</a><br>
+<a href="#vbnet">Will the programs from the site work in VB.NET?</a><br>
+<a href="#vbgoto">Why is the <i>goto</i> operator used in some programs?</a><br>
+</span><br>
+
+<b>General Questions on source codes</b><br>
+
+<span class=smalltext>
+<a href="#reliability">How reliable is the ALGLIB package?</a><br>
+<a href="#ap">What is the AP library?</a><br>
+<a href="#ablas">What is the ABLAS library?</a><br>
+<a href="#statuses">What do module statuses (experimental, development, stable, obsolete) mean?</a><br>
+<a href="#stablevsfull">What is the difference between alglib.stable and alglib.full archives?</a><br>
+<a href="#rcomm">Why do some algorithms (for instance, optimization methods) use reverse communication instead of function pointers, delegates and other means of my programming language?</a><br>
+</span><br>
+
+<b>General questions</b><br>
+
+<span class=smalltext>
+<a href="#project">What is ALGLIB aimed at?</a><br>
+<a href="#diff">What is the difference between ALGLIB and other similar projects?</a><br>
+<a href="#authors">Who are the authors of the ALGLIB?</a><br>
+<a href="#rus">What relation does the website have to Russia?</a><br>
+<a href="#algopascal">What is AlgoPascal?</a><br>
+<a href="#other">Where can I find the algorithms for the work with files, drawing pictures in OpenGL, etc?</a><br>
+</span><br>
+</p>
+
+<hr width=80% size=1>
+
+<h1>C++</h1>
+<a name=stdafx><h2>What is the blank file <code>stdafx.h</code> here for?</h2></a>
+
+<p align=justify>
+MSVC and some other compilers require the <code>#include &lt;stdafx.h&gt;</code> directive in the program code to manage precompiled headers, and create the stfafx.h file when generating a new project. However, some compilers (e.g. BCB) use other tools to manage precompiled headers. In this case the <code>#include &lt;stdafx.h&gt;</code> directive as such doesn't hinder their operation, however if the file with this name is absent, a compilation error occurs. The blank file called <code>stfafx.h</code> is created to avoid this error. If your development environment already created the file, leave it unchanged.
+</p>
+
+<a name=cpparrays><h2>How is the array operations organized in C++?</h2></a>
+
+<p align=justify>
+As there is no dynamic array support in C++ (<b>update:</b> <i>I know about pointers and dynamic memory allocation, but this is not exactly the thing we need here</i>), template classes were developed to work with one- and two-dimensional arrays. The necessary source code and description can be found in the standard AP library, which is supplied with ALGLIB sources. Please note that the array numeration in this library begins from any number the programmer chooses rather than from 0.
+</p>
+
+<a name=cpparrays2><h2>Why are round brackets used when addressing arrays, not square brackets?</h2></a>
+
+<p align=justify>
+Imagine you are addressing the a matrix element in the common notation: <code>a[x][y]</code> instead of <code>a(x,y)</code>. Actually there are two index operators called here instead of one. The first is indexing of a matrix by <code>x</code>, that returns a reference to a temporary structure that describes the matrix row. The second is indexing that temporary structure by <code>y</code>, which returns the reference to the needed element. Addressing through overloading the round brackets is much more effective, as no temporary structures are required.
+</p>
+
+<a name=cpp64><h2>Will the ALGLIB package work in 64-bit environment?</h2></a>
+
+<p align=justify>
+The ALGLIB package was not tested in 64-bit environment.
+</p>
+
+<p align=justify>
+However, the package itself and the AP/C++ library it uses don't contain any code that explicitly points out the environment it should work in. Thus, in theory compiling and usage of ALGLIB package in a 64-bit environment should not cause any trouble when not using additional libraries (like ABLAS, which is at the moment intended to work only in 32-bit environments).
+</p>
+
+<h1>Delphi</h1>
+
+<a name=forwhile><h2>Why is <i>while</i> cycle used instead of the <i>for</i> cycle?</h2></a>
+
+<p align=justify>
+The reason for that is some peculiarities of the <b>for</b> cycle in Delphi. If the cycle has not been executed yet, in many programming languages the control variable of the cycle contains the initial value. In Delphi, if the cycle has not been executed, the control variable of the cycle doesn't change. Once I have seen a source code, in which this difference played a major role. That's why decided to replace the <b>for</b> cycle in Delphi with <b>while</b> cycle, whose behavior is the same as in any other language.
+</p>
+
+<a name=dac><h2>What is the purpose of the <code>DynamicArrayCopy</code> function?</h2></a>
+
+<p align=justify>
+In Delphi dynamic arrays are reference types, i.e. if the array parameter is passed by value, no copying of the array occurs. To emulate the transfer of the array parameter by value we use the <code>DynamicArrayCopy</code> function. It requires a dynamic array as an argument and returns a copy of this array. Assignment of the type <code>A:=DynamicArrayCopy(A)</code> replaces the reference to the original array with a reference to its copy.
+</p>
+
+<h1>Visual Basic</h1>
+
+<a name=vbver><h2>What version of Visual Basic are the algorithms translated into?</h2></a>
+
+<p align=justify>
+The algorithms are translated into VBA, but in general are compatible with VB6. 
+</p>
+
+<a name=vbnet><h2>Will the programs from the site work in VB.NET?</h2></a>
+
+<p align=justify>
+Not unless they are manually ported.
+</p>
+
+<a name=vbgoto><h2>Why is the <i>goto</i> operator used in some programs?</h2></a>
+
+<p align=justify>
+In many programming languages there is control operator <b>continue</b>, but it is absent in VB. In AlgoPascal, this operator appears from time to time. The <b>goto</b> operator is used to replace it and go to the next iteration of the cycle.
+</p>
+
+<h1>General Questions on source codes</h1>
+
+<a name=reliability><h2>How reliable is the ALGLIB package?</h2></a>
+
+<p align=justify>
+The following measures are taken to ensure the reliability of the ALGLIB package:
+</p>
+
+<ul>
+<li>
+testing of package algorithms. All modules that have 'development' or 'stable' status have unit tests. Testing of ALGLIB for C#, C++, C++ (multiple precision) and Delphi is carried out automatically. Unfortunately the VBA version is tested manually and not completely, as VBA programs cannot be launched from command line. To compensate for this, the VBA version is actively used to develop new programs, which allows for counting on its stability.
+</li>
+<li>
+a clear specification of modules limitations.
+</li>
+<li>
+a clear differentiation between stably working modules tested by unit tests and time and experimental modules, where the stability is not tested adequately.
+</li>
+<li>
+transparent bug registration and correction processes.
+</li>
+</ul>
+
+<a name=ap><h2>What is the AP library?</h2></a>
+
+<p align=justify>
+AP library is a generic name for a set of libraries in several programming languages performing low-level tasks depending on specific programming languages. The AP library carries out tasks such as working with dynamic one- and multidimensional arrays in languages which do not support this data type, contains implementation of basic linear algebra algorithms, etc. The library is distributed as source codes under GPL 2+ license (GPL 2 or later). The library is attached to each algorithm package available for downloading from the web-site. The latest library version is available for downloading from <a href="http://alglib.sources.ru/translator/aplib.zip">http://alglib.sources.ru/translator/aplib.zip</a>
+</p>
+
+<a name=ablas><h2>What is the ABLAS library?</h2></a>
+
+<p align=justify>
+ABLAS is an optimized implementation of basic linear algebra subroutines written in assembler. To use ABLAS it is necessary to copy the DLL file to one of the system folders or in the program folder and turn on ABLAS support (as described in ABLAS manual). When launching a program that is using the ALGLIB package, the ABLAS library is automatically detected and loaded. If ABLAS is absent, the program uses the standard implementation of linear algebra operations, written in a high-level language. At <a href="http://www,alglib.net/projects/ablas/">http://www,alglib.net/projects/ablas/</a> (or <a href="http://alglib.sources.ru/projects/ablas/">http://alglib.sources.ru/projects/ablas/</a> in Russian) there is library description, the list of the supported platforms and programming languages as well as source code and precompiled binary files.
+</p>
+
+<a name=stablevsfull><h2>What is the difference between alglib.stable and alglib.full archives?</h2></a>
+
+<p align=justify>
+A 'stable' archive contains only 'stable' modules . A 'full' archive also contains modules with 'experimental' and 'development' status.
+</p>
+
+<a name=statuses><h2>What do module statuses (experimental, development, stable, obsolete) mean?</h2></a>
+
+<p align=justify>
+Modules included into ALGLIB can have a different status depending on the lifecycle stage they are in:
+</p>
+
+<ul>
+<li>
+<B>experimental</B> - the module is experimental and its stability is not guaranteed. It cannot be used in the programs where high level of reliability is a requirement, but still can be useful for library users. You can use it at your own discretion - for instance, for educational purposes. This status is assigned very rarely and only when an unstable module is still better than no module at all. 
+</li>
+<li>
+<B>development</B> - the module is usable, there are no stability issues, but the API that is offered is not yet a hundred per cent completed. It is possible that in future versions of ALGLIB the module will be changed in the manner not compatible with the previous version. This status is assigned pretty seldom and only to new modules. 
+</li>
+<li>
+<B>stable</B> - the module is usable, there are no stability issues, the API that is offered is absolutely done and will not change. Most often the modules are openly published only after they are assigned 'stable' status.
+</li>
+<li>
+<B>obsolete</B> - the module is not supported anymore and not included in the ALGLIB releases.
+</li>
+</ul>
+
+<a name=rcomm><h2>Why do some algorithms (for instance, optimization methods) use reverse communication instead of function pointers, delegates and other means of my programming language?</h2></a>
+
+<p align=justify>
+Optimization, integration and other similar methods are united by one common trait. They need to have a way of calculating the meaning of a function defined by the <I>user</I> at a point defined by the <I>method</I>.
+</p>
+
+<p align=justify>
+The most convenient way of solving this problem is transferring a function pointer into the module. However bear in mind that ALGLIB package is written using pseudo code that is automatically translated into different programming languages. While each language has its own function pointer analog that is often different from other languages. When the ALGLIB pseudo code was developed, at some point is became clear that adding function pointers in it will be very complex as this feature is implemented differently in every language. This is why reverse communication was chosen as a different kind of solution.
+</p>
+
+<h1>General questions</h1>
+
+<a name=project><h2>What is ALGLIB aimed at?</h2></a>
+
+<p align=justify>
+It is mostly aimed at creating a convenient and efficient multilingual scientific software library.
+</p>
+
+<a name=diff><h2>What is the difference between ALGLIB and other similar projects?</h2></a>
+
+<p align=justify>
+The ALGLIB package:
+</p>
+
+<ul>
+<li>is a multilingual project. The main feature of the project is that each algorithm is represented by programs in several languages and the language list is the same for every algorithm. This is the main advantage of the site before other similar collections - one algorithm, several languages, identical functionality in each language.</li>
+<li>is a collection of programs. Sometimes visitors complain that there are practically no algorithm descriptions at the site. Yes, that's true - just because ALGLIB is a collection of programs, not the used algorithm descriptions. Unfortunately we cannot do everything.</li>
+<li>is focused on numerical analysis. There are some other directions in the project (for instance, there are algorithms for sorting and graphics) but numerical analysis is a priority.</li>
+<li>is easy to use. To use the ALGLIB package you don't need to learn an unknown programming language, attach additional external libraries or work with an extensive and inconvenient interface to a code written in another programming language.</li>
+</ul>
+
+<p align=justify>
+Now on what ALGLIB is not and is not going to be. ALGLIB doesn't:
+</p>
+
+<ul>
+<li>compete with specialized projects. This is not the goal. And, however hard I try, the project won't get as fast as MKL or have as wide a functionality as LAPACK.</li>
+<li>go out of the boundaries of the project. For instance, parallel calculations are very interesting, but the solution of this problem is usually tightly connected to a certain platform, programming environment or language, which doesn't align with the main idea of the project.</li>
+</ul>
+
+<a name=authors><h2>Who are the authors of the ALGLIB?</h2></a>
+
+<p align=justify>
+The first version of ALGLIB was made by Vladimir Bystritsky. He spent a number of years on the maintenance of the website by himself. Eventually, he lost his interest in the website, so I (Sergey Bochkanov) took over the work on its maintenance, by common agreement.
+</p>
+
+<a name=rus><h2>What relation does the website have to Russia?</h2></a>
+
+<p align=justify>
+Originally, ALGLIB was a Russian-language website. Later on, a part of the website was translated into English. At present, there are two versions of the site - Russian and English.
+</p>
+
+<a name=algopascal><h2>What is AlgoPascal?</h2></a>
+
+<p align=justify>
+AlgoPascal is a programming language, designed particularly for this project. The programs, written in this language, are processed by a server-side automatic translator and translated into other programming languages by the computer. More detailed information is provided in section <a href="http://www.alglib.net/aboutsite.php">"About the site"</a>.
+</p>
+
+<a name=other><h2>Where can I find the algorithms for the work with files, drawing pictures in OpenGL, etc?</h2></a>
+
+<p align=justify>
+As we noted before, the main goal of ALGLIB is to create a multilingual library of algorithms in the area of numerical analysis. The problems, related to those enumerated in the question, do not fall into this category. Exceptions are made for a number of problems, though the project can be characterized by a clear-cut specialization.
+</p>
+
+</body>
+</html>
Index: trunk/sources/ALGLIB/leastsquares.cs
===================================================================
--- trunk/sources/ALGLIB/leastsquares.cs	(revision 2430)
+++ trunk/sources/ALGLIB/leastsquares.cs	(revision 2430)
@@ -0,0 +1,1305 @@
+/*************************************************************************
+Copyright (c) 2006-2007, Sergey Bochkanov (ALGLIB project).
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class leastsquares
+    {
+        /*************************************************************************
+        Weighted approximation by arbitrary function basis in a space of arbitrary
+        dimension using linear least squares method.
+
+        Input parameters:
+            Y   -   array[0..N-1]
+                    It contains a set  of  function  values  in  N  points.  Space
+                    dimension  and  points  don't  matter.  Procedure  works  with
+                    function values in these points and values of basis  functions
+                    only.
+
+            W   -   array[0..N-1]
+                    It contains weights corresponding  to  function  values.  Each
+                    summand in square sum of approximation deviations  from  given
+                    values is multiplied by the square of corresponding weight.
+
+            FMatrix-a table of basis functions values, array[0..N-1, 0..M-1].
+                    FMatrix[I, J] - value of J-th basis function in I-th point.
+
+            N   -   number of points used. N>=1.
+            M   -   number of basis functions, M>=1.
+
+        Output parameters:
+            C   -   decomposition coefficients.
+                    Array of real numbers whose index goes from 0 to M-1.
+                    C[j] - j-th basis function coefficient.
+
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildgeneralleastsquares(ref double[] y,
+            ref double[] w,
+            ref double[,] fmatrix,
+            int n,
+            int m,
+            ref double[] c)
+        {
+            int i = 0;
+            int j = 0;
+            double[,] a = new double[0,0];
+            double[,] q = new double[0,0];
+            double[,] vt = new double[0,0];
+            double[] b = new double[0];
+            double[] tau = new double[0];
+            double[,] b2 = new double[0,0];
+            double[] tauq = new double[0];
+            double[] taup = new double[0];
+            double[] d = new double[0];
+            double[] e = new double[0];
+            bool isuppera = new bool();
+            int mi = 0;
+            int ni = 0;
+            double v = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            mi = n;
+            ni = m;
+            c = new double[ni-1+1];
+            
+            //
+            // Initialize design matrix.
+            // Here we are making MI>=NI.
+            //
+            a = new double[ni+1, Math.Max(mi, ni)+1];
+            b = new double[Math.Max(mi, ni)+1];
+            for(i=1; i<=mi; i++)
+            {
+                b[i] = w[i-1]*y[i-1];
+            }
+            for(i=mi+1; i<=ni; i++)
+            {
+                b[i] = 0;
+            }
+            for(j=1; j<=ni; j++)
+            {
+                i1_ = (0) - (1);
+                for(i_=1; i_<=mi;i_++)
+                {
+                    a[j,i_] = fmatrix[i_+i1_,j-1];
+                }
+            }
+            for(j=1; j<=ni; j++)
+            {
+                for(i=mi+1; i<=ni; i++)
+                {
+                    a[j,i] = 0;
+                }
+            }
+            for(j=1; j<=ni; j++)
+            {
+                for(i=1; i<=mi; i++)
+                {
+                    a[j,i] = a[j,i]*w[i-1];
+                }
+            }
+            mi = Math.Max(mi, ni);
+            
+            //
+            // LQ-decomposition of A'
+            // B2 := Q*B
+            //
+            lq.lqdecomposition(ref a, ni, mi, ref tau);
+            lq.unpackqfromlq(ref a, ni, mi, ref tau, ni, ref q);
+            b2 = new double[1+1, ni+1];
+            for(j=1; j<=ni; j++)
+            {
+                b2[1,j] = 0;
+            }
+            for(i=1; i<=ni; i++)
+            {
+                v = 0.0;
+                for(i_=1; i_<=mi;i_++)
+                {
+                    v += b[i_]*q[i,i_];
+                }
+                b2[1,i] = v;
+            }
+            
+            //
+            // Back from A' to A
+            // Making cols(A)=rows(A)
+            //
+            for(i=1; i<=ni-1; i++)
+            {
+                for(i_=i+1; i_<=ni;i_++)
+                {
+                    a[i,i_] = a[i_,i];
+                }
+            }
+            for(i=2; i<=ni; i++)
+            {
+                for(j=1; j<=i-1; j++)
+                {
+                    a[i,j] = 0;
+                }
+            }
+            
+            //
+            // Bidiagonal decomposition of A
+            // A = Q * d2 * P'
+            // B2 := (Q'*B2')'
+            //
+            bidiagonal.tobidiagonal(ref a, ni, ni, ref tauq, ref taup);
+            bidiagonal.multiplybyqfrombidiagonal(ref a, ni, ni, ref tauq, ref b2, 1, ni, true, false);
+            bidiagonal.unpackptfrombidiagonal(ref a, ni, ni, ref taup, ni, ref vt);
+            bidiagonal.unpackdiagonalsfrombidiagonal(ref a, ni, ni, ref isuppera, ref d, ref e);
+            
+            //
+            // Singular value decomposition of A
+            // A = U * d * V'
+            // B2 := (U'*B2')'
+            //
+            if( !bdsvd.bidiagonalsvddecomposition(ref d, e, ni, isuppera, false, ref b2, 1, ref q, 0, ref vt, ni) )
+            {
+                for(i=0; i<=ni-1; i++)
+                {
+                    c[i] = 0;
+                }
+                return;
+            }
+            
+            //
+            // B2 := (d^(-1) * B2')'
+            //
+            if( d[1]!=0 )
+            {
+                for(i=1; i<=ni; i++)
+                {
+                    if( d[i]>AP.Math.MachineEpsilon*10*Math.Sqrt(ni)*d[1] )
+                    {
+                        b2[1,i] = b2[1,i]/d[i];
+                    }
+                    else
+                    {
+                        b2[1,i] = 0;
+                    }
+                }
+            }
+            
+            //
+            // B := (V * B2')'
+            //
+            for(i=1; i<=ni; i++)
+            {
+                b[i] = 0;
+            }
+            for(i=1; i<=ni; i++)
+            {
+                v = b2[1,i];
+                for(i_=1; i_<=ni;i_++)
+                {
+                    b[i_] = b[i_] + v*vt[i,i_];
+                }
+            }
+            
+            //
+            // Out
+            //
+            for(i=1; i<=ni; i++)
+            {
+                c[i-1] = b[i];
+            }
+        }
+
+
+        /*************************************************************************
+        Linear approximation using least squares method
+
+        The subroutine calculates coefficients of  the  line  approximating  given
+        function.
+
+        Input parameters:
+            X   -   array[0..N-1], it contains a set of abscissas.
+            Y   -   array[0..N-1], function values.
+            N   -   number of points, N>=1
+
+        Output parameters:
+            a, b-   coefficients of linear approximation a+b*t
+
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildlinearleastsquares(ref double[] x,
+            ref double[] y,
+            int n,
+            ref double a,
+            ref double b)
+        {
+            double pp = 0;
+            double qq = 0;
+            double pq = 0;
+            double b1 = 0;
+            double b2 = 0;
+            double d1 = 0;
+            double d2 = 0;
+            double t1 = 0;
+            double t2 = 0;
+            double phi = 0;
+            double c = 0;
+            double s = 0;
+            double m = 0;
+            int i = 0;
+
+            pp = n;
+            qq = 0;
+            pq = 0;
+            b1 = 0;
+            b2 = 0;
+            for(i=0; i<=n-1; i++)
+            {
+                pq = pq+x[i];
+                qq = qq+AP.Math.Sqr(x[i]);
+                b1 = b1+y[i];
+                b2 = b2+x[i]*y[i];
+            }
+            phi = Math.Atan2(2*pq, qq-pp)/2;
+            c = Math.Cos(phi);
+            s = Math.Sin(phi);
+            d1 = AP.Math.Sqr(c)*pp+AP.Math.Sqr(s)*qq-2*s*c*pq;
+            d2 = AP.Math.Sqr(s)*pp+AP.Math.Sqr(c)*qq+2*s*c*pq;
+            if( Math.Abs(d1)>Math.Abs(d2) )
+            {
+                m = Math.Abs(d1);
+            }
+            else
+            {
+                m = Math.Abs(d2);
+            }
+            t1 = c*b1-s*b2;
+            t2 = s*b1+c*b2;
+            if( Math.Abs(d1)>m*AP.Math.MachineEpsilon*1000 )
+            {
+                t1 = t1/d1;
+            }
+            else
+            {
+                t1 = 0;
+            }
+            if( Math.Abs(d2)>m*AP.Math.MachineEpsilon*1000 )
+            {
+                t2 = t2/d2;
+            }
+            else
+            {
+                t2 = 0;
+            }
+            a = c*t1+s*t2;
+            b = -(s*t1)+c*t2;
+        }
+
+
+        /*************************************************************************
+        Weighted cubic spline approximation using linear least squares
+
+        Input parameters:
+            X   -   array[0..N-1], abscissas
+            Y   -   array[0..N-1], function values
+            W   -   array[0..N-1], weights.
+            A, B-   interval to build splines in.
+            N   -   number of points used. N>=1.
+            M   -   number of basic splines, M>=2.
+
+        Output parameters:
+            CTbl-   coefficients table to be used by SplineInterpolation function.
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildsplineleastsquares(ref double[] x,
+            ref double[] y,
+            ref double[] w,
+            double a,
+            double b,
+            int n,
+            int m,
+            ref double[] ctbl)
+        {
+            int i = 0;
+            int j = 0;
+            double[,] ma = new double[0,0];
+            double[,] q = new double[0,0];
+            double[,] vt = new double[0,0];
+            double[] mb = new double[0];
+            double[] tau = new double[0];
+            double[,] b2 = new double[0,0];
+            double[] tauq = new double[0];
+            double[] taup = new double[0];
+            double[] d = new double[0];
+            double[] e = new double[0];
+            bool isuppera = new bool();
+            int mi = 0;
+            int ni = 0;
+            double v = 0;
+            double[] sx = new double[0];
+            double[] sy = new double[0];
+            int i_ = 0;
+
+            System.Diagnostics.Debug.Assert(m>=2, "BuildSplineLeastSquares: M is too small!");
+            mi = n;
+            ni = m;
+            sx = new double[ni-1+1];
+            sy = new double[ni-1+1];
+            
+            //
+            // Initializing design matrix
+            // Here we are making MI>=NI
+            //
+            ma = new double[ni+1, Math.Max(mi, ni)+1];
+            mb = new double[Math.Max(mi, ni)+1];
+            for(j=0; j<=ni-1; j++)
+            {
+                sx[j] = a+(b-a)*j/(ni-1);
+            }
+            for(j=0; j<=ni-1; j++)
+            {
+                for(i=0; i<=ni-1; i++)
+                {
+                    sy[i] = 0;
+                }
+                sy[j] = 1;
+                spline3.buildcubicspline(sx, sy, ni, 0, 0.0, 0, 0.0, ref ctbl);
+                for(i=0; i<=mi-1; i++)
+                {
+                    ma[j+1,i+1] = w[i]*spline3.splineinterpolation(ref ctbl, x[i]);
+                }
+            }
+            for(j=1; j<=ni; j++)
+            {
+                for(i=mi+1; i<=ni; i++)
+                {
+                    ma[j,i] = 0;
+                }
+            }
+            
+            //
+            // Initializing right part
+            //
+            for(i=0; i<=mi-1; i++)
+            {
+                mb[i+1] = w[i]*y[i];
+            }
+            for(i=mi+1; i<=ni; i++)
+            {
+                mb[i] = 0;
+            }
+            mi = Math.Max(mi, ni);
+            
+            //
+            // LQ-decomposition of A'
+            // B2 := Q*B
+            //
+            lq.lqdecomposition(ref ma, ni, mi, ref tau);
+            lq.unpackqfromlq(ref ma, ni, mi, ref tau, ni, ref q);
+            b2 = new double[1+1, ni+1];
+            for(j=1; j<=ni; j++)
+            {
+                b2[1,j] = 0;
+            }
+            for(i=1; i<=ni; i++)
+            {
+                v = 0.0;
+                for(i_=1; i_<=mi;i_++)
+                {
+                    v += mb[i_]*q[i,i_];
+                }
+                b2[1,i] = v;
+            }
+            
+            //
+            // Back from A' to A
+            // Making cols(A)=rows(A)
+            //
+            for(i=1; i<=ni-1; i++)
+            {
+                for(i_=i+1; i_<=ni;i_++)
+                {
+                    ma[i,i_] = ma[i_,i];
+                }
+            }
+            for(i=2; i<=ni; i++)
+            {
+                for(j=1; j<=i-1; j++)
+                {
+                    ma[i,j] = 0;
+                }
+            }
+            
+            //
+            // Bidiagonal decomposition of A
+            // A = Q * d2 * P'
+            // B2 := (Q'*B2')'
+            //
+            bidiagonal.tobidiagonal(ref ma, ni, ni, ref tauq, ref taup);
+            bidiagonal.multiplybyqfrombidiagonal(ref ma, ni, ni, ref tauq, ref b2, 1, ni, true, false);
+            bidiagonal.unpackptfrombidiagonal(ref ma, ni, ni, ref taup, ni, ref vt);
+            bidiagonal.unpackdiagonalsfrombidiagonal(ref ma, ni, ni, ref isuppera, ref d, ref e);
+            
+            //
+            // Singular value decomposition of A
+            // A = U * d * V'
+            // B2 := (U'*B2')'
+            //
+            if( !bdsvd.bidiagonalsvddecomposition(ref d, e, ni, isuppera, false, ref b2, 1, ref q, 0, ref vt, ni) )
+            {
+                for(i=1; i<=ni; i++)
+                {
+                    d[i] = 0;
+                    b2[1,i] = 0;
+                    for(j=1; j<=ni; j++)
+                    {
+                        if( i==j )
+                        {
+                            vt[i,j] = 1;
+                        }
+                        else
+                        {
+                            vt[i,j] = 0;
+                        }
+                    }
+                }
+                b2[1,1] = 1;
+            }
+            
+            //
+            // B2 := (d^(-1) * B2')'
+            //
+            for(i=1; i<=ni; i++)
+            {
+                if( d[i]>AP.Math.MachineEpsilon*10*Math.Sqrt(ni)*d[1] )
+                {
+                    b2[1,i] = b2[1,i]/d[i];
+                }
+                else
+                {
+                    b2[1,i] = 0;
+                }
+            }
+            
+            //
+            // B := (V * B2')'
+            //
+            for(i=1; i<=ni; i++)
+            {
+                mb[i] = 0;
+            }
+            for(i=1; i<=ni; i++)
+            {
+                v = b2[1,i];
+                for(i_=1; i_<=ni;i_++)
+                {
+                    mb[i_] = mb[i_] + v*vt[i,i_];
+                }
+            }
+            
+            //
+            // Forming result spline
+            //
+            for(i=0; i<=ni-1; i++)
+            {
+                sy[i] = mb[i+1];
+            }
+            spline3.buildcubicspline(sx, sy, ni, 0, 0.0, 0, 0.0, ref ctbl);
+        }
+
+
+        /*************************************************************************
+        Polynomial approximation using least squares method
+
+        The subroutine calculates coefficients  of  the  polynomial  approximating
+        given function. It is recommended to use this function only if you need to
+        obtain coefficients of approximation polynomial. If you have to build  and
+        calculate polynomial approximation (NOT coefficients), it's better to  use
+        BuildChebyshevLeastSquares      subroutine     in     combination     with
+        CalculateChebyshevLeastSquares   subroutine.   The   result  of  Chebyshev
+        polynomial approximation is equivalent to the result obtained using powers
+        of X, but has higher  accuracy  due  to  better  numerical  properties  of
+        Chebyshev polynomials.
+
+        Input parameters:
+            X   -   array[0..N-1], abscissas
+            Y   -   array[0..N-1], function values
+            N   -   number of points, N>=1
+            M   -   order of polynomial required, M>=0
+
+        Output parameters:
+            C   -   approximating polynomial coefficients, array[0..M],
+                    C[i] - coefficient at X^i.
+
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildpolynomialleastsquares(ref double[] x,
+            ref double[] y,
+            int n,
+            int m,
+            ref double[] c)
+        {
+            double[] ctbl = new double[0];
+            double[] w = new double[0];
+            double[] c1 = new double[0];
+            double maxx = 0;
+            double minx = 0;
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            double e = 0;
+            double d = 0;
+            double l1 = 0;
+            double l2 = 0;
+            double[] z2 = new double[0];
+            double[] z1 = new double[0];
+
+            
+            //
+            // Initialize
+            //
+            maxx = x[0];
+            minx = x[0];
+            for(i=1; i<=n-1; i++)
+            {
+                if( x[i]>maxx )
+                {
+                    maxx = x[i];
+                }
+                if( x[i]<minx )
+                {
+                    minx = x[i];
+                }
+            }
+            if( minx==maxx )
+            {
+                minx = minx-0.5;
+                maxx = maxx+0.5;
+            }
+            w = new double[n-1+1];
+            for(i=0; i<=n-1; i++)
+            {
+                w[i] = 1;
+            }
+            
+            //
+            // Build Chebyshev approximation
+            //
+            buildchebyshevleastsquares(ref x, ref y, ref w, minx, maxx, n, m, ref ctbl);
+            
+            //
+            // From Chebyshev to powers of X
+            //
+            c1 = new double[m+1];
+            for(i=0; i<=m; i++)
+            {
+                c1[i] = 0;
+            }
+            d = 0;
+            for(i=0; i<=m; i++)
+            {
+                for(k=i; k<=m; k++)
+                {
+                    e = c1[k];
+                    c1[k] = 0;
+                    if( i<=1 & k==i )
+                    {
+                        c1[k] = 1;
+                    }
+                    else
+                    {
+                        if( i!=0 )
+                        {
+                            c1[k] = 2*d;
+                        }
+                        if( k>i+1 )
+                        {
+                            c1[k] = c1[k]-c1[k-2];
+                        }
+                    }
+                    d = e;
+                }
+                d = c1[i];
+                e = 0;
+                k = i;
+                while( k<=m )
+                {
+                    e = e+c1[k]*ctbl[k];
+                    k = k+2;
+                }
+                c1[i] = e;
+            }
+            
+            //
+            // Linear translation
+            //
+            l1 = 2/(ctbl[m+2]-ctbl[m+1]);
+            l2 = -(2*ctbl[m+1]/(ctbl[m+2]-ctbl[m+1]))-1;
+            c = new double[m+1];
+            z2 = new double[m+1];
+            z1 = new double[m+1];
+            c[0] = c1[0];
+            z1[0] = 1;
+            z2[0] = 1;
+            for(i=1; i<=m; i++)
+            {
+                z2[i] = 1;
+                z1[i] = l2*z1[i-1];
+                c[0] = c[0]+c1[i]*z1[i];
+            }
+            for(j=1; j<=m; j++)
+            {
+                z2[0] = l1*z2[0];
+                c[j] = c1[j]*z2[0];
+                for(i=j+1; i<=m; i++)
+                {
+                    k = i-j;
+                    z2[k] = l1*z2[k]+z2[k-1];
+                    c[j] = c[j]+c1[i]*z2[k]*z1[k];
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Chebyshev polynomial approximation using least squares method.
+
+        The algorithm reduces interval [A, B] to the interval [-1,1], then  builds
+        least squares approximation using Chebyshev polynomials.
+
+        Input parameters:
+            X   -   array[0..N-1], abscissas
+            Y   -   array[0..N-1], function values
+            W   -   array[0..N-1], weights
+            A, B-   interval to build approximating polynomials in.
+            N   -   number of points used. N>=1.
+            M   -   order of polynomial, M>=0. This parameter is passed into
+                    CalculateChebyshevLeastSquares function.
+
+        Output parameters:
+            CTbl - coefficient table. This parameter is passed into
+                    CalculateChebyshevLeastSquares function.
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildchebyshevleastsquares(ref double[] x,
+            ref double[] y,
+            ref double[] w,
+            double a,
+            double b,
+            int n,
+            int m,
+            ref double[] ctbl)
+        {
+            int i = 0;
+            int j = 0;
+            double[,] ma = new double[0,0];
+            double[,] q = new double[0,0];
+            double[,] vt = new double[0,0];
+            double[] mb = new double[0];
+            double[] tau = new double[0];
+            double[,] b2 = new double[0,0];
+            double[] tauq = new double[0];
+            double[] taup = new double[0];
+            double[] d = new double[0];
+            double[] e = new double[0];
+            bool isuppera = new bool();
+            int mi = 0;
+            int ni = 0;
+            double v = 0;
+            int i_ = 0;
+
+            mi = n;
+            ni = m+1;
+            
+            //
+            // Initializing design matrix
+            // Here we are making MI>=NI
+            //
+            ma = new double[ni+1, Math.Max(mi, ni)+1];
+            mb = new double[Math.Max(mi, ni)+1];
+            for(j=1; j<=ni; j++)
+            {
+                for(i=1; i<=mi; i++)
+                {
+                    v = 2*(x[i-1]-a)/(b-a)-1;
+                    if( j==1 )
+                    {
+                        ma[j,i] = 1.0;
+                    }
+                    if( j==2 )
+                    {
+                        ma[j,i] = v;
+                    }
+                    if( j>2 )
+                    {
+                        ma[j,i] = 2.0*v*ma[j-1,i]-ma[j-2,i];
+                    }
+                }
+            }
+            for(j=1; j<=ni; j++)
+            {
+                for(i=1; i<=mi; i++)
+                {
+                    ma[j,i] = w[i-1]*ma[j,i];
+                }
+            }
+            for(j=1; j<=ni; j++)
+            {
+                for(i=mi+1; i<=ni; i++)
+                {
+                    ma[j,i] = 0;
+                }
+            }
+            
+            //
+            // Initializing right part
+            //
+            for(i=0; i<=mi-1; i++)
+            {
+                mb[i+1] = w[i]*y[i];
+            }
+            for(i=mi+1; i<=ni; i++)
+            {
+                mb[i] = 0;
+            }
+            mi = Math.Max(mi, ni);
+            
+            //
+            // LQ-decomposition of A'
+            // B2 := Q*B
+            //
+            lq.lqdecomposition(ref ma, ni, mi, ref tau);
+            lq.unpackqfromlq(ref ma, ni, mi, ref tau, ni, ref q);
+            b2 = new double[1+1, ni+1];
+            for(j=1; j<=ni; j++)
+            {
+                b2[1,j] = 0;
+            }
+            for(i=1; i<=ni; i++)
+            {
+                v = 0.0;
+                for(i_=1; i_<=mi;i_++)
+                {
+                    v += mb[i_]*q[i,i_];
+                }
+                b2[1,i] = v;
+            }
+            
+            //
+            // Back from A' to A
+            // Making cols(A)=rows(A)
+            //
+            for(i=1; i<=ni-1; i++)
+            {
+                for(i_=i+1; i_<=ni;i_++)
+                {
+                    ma[i,i_] = ma[i_,i];
+                }
+            }
+            for(i=2; i<=ni; i++)
+            {
+                for(j=1; j<=i-1; j++)
+                {
+                    ma[i,j] = 0;
+                }
+            }
+            
+            //
+            // Bidiagonal decomposition of A
+            // A = Q * d2 * P'
+            // B2 := (Q'*B2')'
+            //
+            bidiagonal.tobidiagonal(ref ma, ni, ni, ref tauq, ref taup);
+            bidiagonal.multiplybyqfrombidiagonal(ref ma, ni, ni, ref tauq, ref b2, 1, ni, true, false);
+            bidiagonal.unpackptfrombidiagonal(ref ma, ni, ni, ref taup, ni, ref vt);
+            bidiagonal.unpackdiagonalsfrombidiagonal(ref ma, ni, ni, ref isuppera, ref d, ref e);
+            
+            //
+            // Singular value decomposition of A
+            // A = U * d * V'
+            // B2 := (U'*B2')'
+            //
+            if( !bdsvd.bidiagonalsvddecomposition(ref d, e, ni, isuppera, false, ref b2, 1, ref q, 0, ref vt, ni) )
+            {
+                for(i=1; i<=ni; i++)
+                {
+                    d[i] = 0;
+                    b2[1,i] = 0;
+                    for(j=1; j<=ni; j++)
+                    {
+                        if( i==j )
+                        {
+                            vt[i,j] = 1;
+                        }
+                        else
+                        {
+                            vt[i,j] = 0;
+                        }
+                    }
+                }
+                b2[1,1] = 1;
+            }
+            
+            //
+            // B2 := (d^(-1) * B2')'
+            //
+            for(i=1; i<=ni; i++)
+            {
+                if( d[i]>AP.Math.MachineEpsilon*10*Math.Sqrt(ni)*d[1] )
+                {
+                    b2[1,i] = b2[1,i]/d[i];
+                }
+                else
+                {
+                    b2[1,i] = 0;
+                }
+            }
+            
+            //
+            // B := (V * B2')'
+            //
+            for(i=1; i<=ni; i++)
+            {
+                mb[i] = 0;
+            }
+            for(i=1; i<=ni; i++)
+            {
+                v = b2[1,i];
+                for(i_=1; i_<=ni;i_++)
+                {
+                    mb[i_] = mb[i_] + v*vt[i,i_];
+                }
+            }
+            
+            //
+            // Forming result
+            //
+            ctbl = new double[ni+1+1];
+            for(i=1; i<=ni; i++)
+            {
+                ctbl[i-1] = mb[i];
+            }
+            ctbl[ni] = a;
+            ctbl[ni+1] = b;
+        }
+
+
+        /*************************************************************************
+        Weighted Chebyshev polynomial constrained least squares approximation.
+
+        The algorithm reduces [A,B] to [-1,1] and builds the Chebyshev polynomials
+        series by approximating a given function using the least squares method.
+
+        Input parameters:
+            X   -   abscissas, array[0..N-1]
+            Y   -   function values, array[0..N-1]
+            W   -   weights, array[0..N-1].  Each  item  in  the  squared  sum  of
+                    deviations from given values is  multiplied  by  a  square  of
+                    corresponding weight.
+            A, B-   interval in which the approximating polynomials are built.
+            N   -   number of points, N>0.
+            XC, YC, DC-
+                    constraints (see description below)., array[0..NC-1]
+            NC  -   number of constraints. 0 <= NC < M+1.
+            M   -   degree of polynomial, M>=0. This parameter is passed into  the
+                    CalculateChebyshevLeastSquares subroutine.
+
+        Output parameters:
+            CTbl-   coefficient  table.  This  parameter  is   passed   into   the
+                    CalculateChebyshevLeastSquares subroutine.
+
+        Result:
+            True, if the algorithm succeeded.
+            False, if the internal singular value decomposition subroutine  hasn't
+        converged or the given constraints could not be met  simultaneously  (e.g.
+        P(0)=0 è P(0)=1).
+
+        Specifying constraints:
+            This subroutine can solve  the  problem  having  constrained  function
+        values or its derivatives in several points. NC specifies  the  number  of
+        constraints, DC - the type of constraints, XC and YC - constraints as such.
+        Thus, for each i from 0 to NC-1 the following constraint is given:
+            P(xc[i]) = yc[i],       if DC[i]=0
+        or
+            d/dx(P(xc[i])) = yc[i], if DC[i]=1
+        (here P(x) is approximating polynomial).
+            This version of the subroutine supports only either polynomial or  its
+        derivative value constraints.  If  DC[i]  is  not  equal  to  0 and 1, the
+        subroutine will be aborted. The number of constraints should be less  than
+        the number of degrees of freedom of approximating  polynomial  -  M+1  (at
+        that, it could be equal to 0).
+
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static bool buildchebyshevleastsquaresconstrained(ref double[] x,
+            ref double[] y,
+            ref double[] w,
+            double a,
+            double b,
+            int n,
+            ref double[] xc,
+            ref double[] yc,
+            ref int[] dc,
+            int nc,
+            int m,
+            ref double[] ctbl)
+        {
+            bool result = new bool();
+            int i = 0;
+            int j = 0;
+            int reducedsize = 0;
+            double[,] designmatrix = new double[0,0];
+            double[] rightpart = new double[0];
+            double[,] cmatrix = new double[0,0];
+            double[,] c = new double[0,0];
+            double[,] u = new double[0,0];
+            double[,] vt = new double[0,0];
+            double[] d = new double[0];
+            double[] cr = new double[0];
+            double[] ws = new double[0];
+            double[] tj = new double[0];
+            double[] uj = new double[0];
+            double[] dtj = new double[0];
+            double[] tmp = new double[0];
+            double[] tmp2 = new double[0];
+            double[,] tmpmatrix = new double[0,0];
+            double v = 0;
+            int i_ = 0;
+
+            System.Diagnostics.Debug.Assert(n>0);
+            System.Diagnostics.Debug.Assert(m>=0);
+            System.Diagnostics.Debug.Assert(nc>=0 & nc<m+1);
+            result = true;
+            
+            //
+            // Initialize design matrix and right part.
+            // Add fictional rows if needed to ensure that N>=M+1.
+            //
+            designmatrix = new double[Math.Max(n, m+1)+1, m+1+1];
+            rightpart = new double[Math.Max(n, m+1)+1];
+            for(i=1; i<=n; i++)
+            {
+                for(j=1; j<=m+1; j++)
+                {
+                    v = 2*(x[i-1]-a)/(b-a)-1;
+                    if( j==1 )
+                    {
+                        designmatrix[i,j] = 1.0;
+                    }
+                    if( j==2 )
+                    {
+                        designmatrix[i,j] = v;
+                    }
+                    if( j>2 )
+                    {
+                        designmatrix[i,j] = 2.0*v*designmatrix[i,j-1]-designmatrix[i,j-2];
+                    }
+                }
+            }
+            for(i=1; i<=n; i++)
+            {
+                for(j=1; j<=m+1; j++)
+                {
+                    designmatrix[i,j] = w[i-1]*designmatrix[i,j];
+                }
+            }
+            for(i=n+1; i<=m+1; i++)
+            {
+                for(j=1; j<=m+1; j++)
+                {
+                    designmatrix[i,j] = 0;
+                }
+            }
+            for(i=0; i<=n-1; i++)
+            {
+                rightpart[i+1] = w[i]*y[i];
+            }
+            for(i=n+1; i<=m+1; i++)
+            {
+                rightpart[i] = 0;
+            }
+            n = Math.Max(n, m+1);
+            
+            //
+            // Now N>=M+1 and we are ready to the next stage.
+            // Handle constraints.
+            // Represent feasible set of coefficients as x = C*t + d
+            //
+            c = new double[m+1+1, m+1+1];
+            d = new double[m+1+1];
+            if( nc==0 )
+            {
+                
+                //
+                // No constraints
+                //
+                for(i=1; i<=m+1; i++)
+                {
+                    for(j=1; j<=m+1; j++)
+                    {
+                        c[i,j] = 0;
+                    }
+                    d[i] = 0;
+                }
+                for(i=1; i<=m+1; i++)
+                {
+                    c[i,i] = 1;
+                }
+                reducedsize = m+1;
+            }
+            else
+            {
+                
+                //
+                // Constraints are present.
+                // Fill constraints matrix CMatrix and solve CMatrix*x = cr.
+                //
+                cmatrix = new double[nc+1, m+1+1];
+                cr = new double[nc+1];
+                tj = new double[m+1];
+                uj = new double[m+1];
+                dtj = new double[m+1];
+                for(i=0; i<=nc-1; i++)
+                {
+                    v = 2*(xc[i]-a)/(b-a)-1;
+                    for(j=0; j<=m; j++)
+                    {
+                        if( j==0 )
+                        {
+                            tj[j] = 1;
+                            uj[j] = 1;
+                            dtj[j] = 0;
+                        }
+                        if( j==1 )
+                        {
+                            tj[j] = v;
+                            uj[j] = 2*v;
+                            dtj[j] = 1;
+                        }
+                        if( j>1 )
+                        {
+                            tj[j] = 2*v*tj[j-1]-tj[j-2];
+                            uj[j] = 2*v*uj[j-1]-uj[j-2];
+                            dtj[j] = j*uj[j-1];
+                        }
+                        System.Diagnostics.Debug.Assert(dc[i]==0 | dc[i]==1);
+                        if( dc[i]==0 )
+                        {
+                            cmatrix[i+1,j+1] = tj[j];
+                        }
+                        if( dc[i]==1 )
+                        {
+                            cmatrix[i+1,j+1] = dtj[j];
+                        }
+                    }
+                    cr[i+1] = yc[i];
+                }
+                
+                //
+                // Solve CMatrix*x = cr.
+                // Fill C and d:
+                // 1. SVD: CMatrix = U * WS * V^T
+                // 2. C := V[1:M+1,NC+1:M+1]
+                // 3. tmp := WS^-1 * U^T * cr
+                // 4. d := V[1:M+1,1:NC] * tmp
+                //
+                if( !svd.svddecomposition(cmatrix, nc, m+1, 2, 2, 2, ref ws, ref u, ref vt) )
+                {
+                    result = false;
+                    return result;
+                }
+                if( ws[1]==0 | ws[nc]<=AP.Math.MachineEpsilon*10*Math.Sqrt(nc)*ws[1] )
+                {
+                    result = false;
+                    return result;
+                }
+                c = new double[m+1+1, m+1-nc+1];
+                d = new double[m+1+1];
+                for(i=1; i<=m+1-nc; i++)
+                {
+                    for(i_=1; i_<=m+1;i_++)
+                    {
+                        c[i_,i] = vt[nc+i,i_];
+                    }
+                }
+                tmp = new double[nc+1];
+                for(i=1; i<=nc; i++)
+                {
+                    v = 0.0;
+                    for(i_=1; i_<=nc;i_++)
+                    {
+                        v += u[i_,i]*cr[i_];
+                    }
+                    tmp[i] = v/ws[i];
+                }
+                for(i=1; i<=m+1; i++)
+                {
+                    d[i] = 0;
+                }
+                for(i=1; i<=nc; i++)
+                {
+                    v = tmp[i];
+                    for(i_=1; i_<=m+1;i_++)
+                    {
+                        d[i_] = d[i_] + v*vt[i,i_];
+                    }
+                }
+                
+                //
+                // Reduce problem:
+                // 1. RightPart := RightPart - DesignMatrix*d
+                // 2. DesignMatrix := DesignMatrix*C
+                //
+                for(i=1; i<=n; i++)
+                {
+                    v = 0.0;
+                    for(i_=1; i_<=m+1;i_++)
+                    {
+                        v += designmatrix[i,i_]*d[i_];
+                    }
+                    rightpart[i] = rightpart[i]-v;
+                }
+                reducedsize = m+1-nc;
+                tmpmatrix = new double[n+1, reducedsize+1];
+                tmp = new double[n+1];
+                blas.matrixmatrixmultiply(ref designmatrix, 1, n, 1, m+1, false, ref c, 1, m+1, 1, reducedsize, false, 1.0, ref tmpmatrix, 1, n, 1, reducedsize, 0.0, ref tmp);
+                blas.copymatrix(ref tmpmatrix, 1, n, 1, reducedsize, ref designmatrix, 1, n, 1, reducedsize);
+            }
+            
+            //
+            // Solve reduced problem DesignMatrix*t = RightPart.
+            //
+            if( !svd.svddecomposition(designmatrix, n, reducedsize, 1, 1, 2, ref ws, ref u, ref vt) )
+            {
+                result = false;
+                return result;
+            }
+            tmp = new double[reducedsize+1];
+            tmp2 = new double[reducedsize+1];
+            for(i=1; i<=reducedsize; i++)
+            {
+                tmp[i] = 0;
+            }
+            for(i=1; i<=n; i++)
+            {
+                v = rightpart[i];
+                for(i_=1; i_<=reducedsize;i_++)
+                {
+                    tmp[i_] = tmp[i_] + v*u[i,i_];
+                }
+            }
+            for(i=1; i<=reducedsize; i++)
+            {
+                if( ws[i]!=0 & ws[i]>AP.Math.MachineEpsilon*10*Math.Sqrt(nc)*ws[1] )
+                {
+                    tmp[i] = tmp[i]/ws[i];
+                }
+                else
+                {
+                    tmp[i] = 0;
+                }
+            }
+            for(i=1; i<=reducedsize; i++)
+            {
+                tmp2[i] = 0;
+            }
+            for(i=1; i<=reducedsize; i++)
+            {
+                v = tmp[i];
+                for(i_=1; i_<=reducedsize;i_++)
+                {
+                    tmp2[i_] = tmp2[i_] + v*vt[i,i_];
+                }
+            }
+            
+            //
+            // Solution is in the tmp2.
+            // Transform it from t to x.
+            //
+            ctbl = new double[m+2+1];
+            for(i=1; i<=m+1; i++)
+            {
+                v = 0.0;
+                for(i_=1; i_<=reducedsize;i_++)
+                {
+                    v += c[i,i_]*tmp2[i_];
+                }
+                ctbl[i-1] = v+d[i];
+            }
+            ctbl[m+1] = a;
+            ctbl[m+2] = b;
+            return result;
+        }
+
+
+        /*************************************************************************
+        Calculation of a Chebyshev  polynomial  obtained   during  least  squares
+        approximaion at the given point.
+
+        Input parameters:
+            M   -   order of polynomial (parameter of the
+                    BuildChebyshevLeastSquares function).
+            A   -   coefficient table.
+                    A[0..M] contains coefficients of the i-th Chebyshev polynomial.
+                    A[M+1] contains left boundary of approximation interval.
+                    A[M+2] contains right boundary of approximation interval.
+            X   -   point to perform calculations in.
+
+        The result is the value at the given point.
+
+        It should be noted that array A contains coefficients  of  the  Chebyshev
+        polynomials defined on interval [-1,1].   Argument  is  reduced  to  this
+        interval before calculating polynomial value.
+          -- ALGLIB --
+             Copyright by Bochkanov Sergey
+        *************************************************************************/
+        public static double calculatechebyshevleastsquares(int m,
+            ref double[] a,
+            double x)
+        {
+            double result = 0;
+            double b1 = 0;
+            double b2 = 0;
+            int i = 0;
+
+            x = 2*(x-a[m+1])/(a[m+2]-a[m+1])-1;
+            b1 = 0;
+            b2 = 0;
+            i = m;
+            do
+            {
+                result = 2*x*b1-b2+a[i];
+                b2 = b1;
+                b1 = result;
+                i = i-1;
+            }
+            while( i>=0 );
+            result = result-x*b2;
+            return result;
+        }
+    }
+}
Index: trunk/sources/ALGLIB/lq.cs
===================================================================
--- trunk/sources/ALGLIB/lq.cs	(revision 2430)
+++ trunk/sources/ALGLIB/lq.cs	(revision 2430)
@@ -0,0 +1,433 @@
+/*************************************************************************
+Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project).
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class lq
+    {
+        /*************************************************************************
+        LQ decomposition of a rectangular matrix of size MxN
+
+        Input parameters:
+            A   -   matrix A whose indexes range within [0..M-1, 0..N-1].
+            M   -   number of rows in matrix A.
+            N   -   number of columns in matrix A.
+
+        Output parameters:
+            A   -   matrices L and Q in compact form (see below)
+            Tau -   array of scalar factors which are used to form
+                    matrix Q. Array whose index ranges within [0..Min(M,N)-1].
+
+        Matrix A is represented as A = LQ, where Q is an orthogonal matrix of size
+        MxM, L - lower triangular (or lower trapezoid) matrix of size M x N.
+
+        The elements of matrix L are located on and below  the  main  diagonal  of
+        matrix A. The elements which are located in Tau array and above  the  main
+        diagonal of matrix A are used to form matrix Q as follows:
+
+        Matrix Q is represented as a product of elementary reflections
+
+        Q = H(k-1)*H(k-2)*...*H(1)*H(0),
+
+        where k = min(m,n), and each H(i) is of the form
+
+        H(i) = 1 - tau * v * (v^T)
+
+        where tau is a scalar stored in Tau[I]; v - real vector, so that v(0:i-1)=0,
+        v(i) = 1, v(i+1:n-1) stored in A(i,i+1:n-1).
+
+          -- ALGLIB --
+             Copyright 2005-2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixlq(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau)
+        {
+            double[] work = new double[0];
+            double[] t = new double[0];
+            int i = 0;
+            int k = 0;
+            int minmn = 0;
+            int maxmn = 0;
+            double tmp = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            minmn = Math.Min(m, n);
+            maxmn = Math.Max(m, n);
+            work = new double[m+1];
+            t = new double[n+1];
+            tau = new double[minmn-1+1];
+            k = Math.Min(m, n);
+            for(i=0; i<=k-1; i++)
+            {
+                
+                //
+                // Generate elementary reflector H(i) to annihilate A(i,i+1:n-1)
+                //
+                i1_ = (i) - (1);
+                for(i_=1; i_<=n-i;i_++)
+                {
+                    t[i_] = a[i,i_+i1_];
+                }
+                reflections.generatereflection(ref t, n-i, ref tmp);
+                tau[i] = tmp;
+                i1_ = (1) - (i);
+                for(i_=i; i_<=n-1;i_++)
+                {
+                    a[i,i_] = t[i_+i1_];
+                }
+                t[1] = 1;
+                if( i<n )
+                {
+                    
+                    //
+                    // Apply H(i) to A(i+1:m,i:n) from the right
+                    //
+                    reflections.applyreflectionfromtheright(ref a, tau[i], ref t, i+1, m-1, i, n-1, ref work);
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Partial unpacking of matrix Q from the LQ decomposition of a matrix A
+
+        Input parameters:
+            A       -   matrices L and Q in compact form.
+                        Output of RMatrixLQ subroutine.
+            M       -   number of rows in given matrix A. M>=0.
+            N       -   number of columns in given matrix A. N>=0.
+            Tau     -   scalar factors which are used to form Q.
+                        Output of the RMatrixLQ subroutine.
+            QRows   -   required number of rows in matrix Q. N>=QRows>=0.
+
+        Output parameters:
+            Q       -   first QRows rows of matrix Q. Array whose indexes range
+                        within [0..QRows-1, 0..N-1]. If QRows=0, the array remains
+                        unchanged.
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixlqunpackq(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau,
+            int qrows,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int minmn = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int i_ = 0;
+            int i1_ = 0;
+
+            System.Diagnostics.Debug.Assert(qrows<=n, "RMatrixLQUnpackQ: QRows>N!");
+            if( m<=0 | n<=0 | qrows<=0 )
+            {
+                return;
+            }
+            
+            //
+            // init
+            //
+            minmn = Math.Min(m, n);
+            k = Math.Min(minmn, qrows);
+            q = new double[qrows-1+1, n-1+1];
+            v = new double[n+1];
+            work = new double[qrows+1];
+            for(i=0; i<=qrows-1; i++)
+            {
+                for(j=0; j<=n-1; j++)
+                {
+                    if( i==j )
+                    {
+                        q[i,j] = 1;
+                    }
+                    else
+                    {
+                        q[i,j] = 0;
+                    }
+                }
+            }
+            
+            //
+            // unpack Q
+            //
+            for(i=k-1; i>=0; i--)
+            {
+                
+                //
+                // Apply H(i)
+                //
+                i1_ = (i) - (1);
+                for(i_=1; i_<=n-i;i_++)
+                {
+                    v[i_] = a[i,i_+i1_];
+                }
+                v[1] = 1;
+                reflections.applyreflectionfromtheright(ref q, tau[i], ref v, 0, qrows-1, i, n-1, ref work);
+            }
+        }
+
+
+        /*************************************************************************
+        Unpacking of matrix L from the LQ decomposition of a matrix A
+
+        Input parameters:
+            A       -   matrices Q and L in compact form.
+                        Output of RMatrixLQ subroutine.
+            M       -   number of rows in given matrix A. M>=0.
+            N       -   number of columns in given matrix A. N>=0.
+
+        Output parameters:
+            L       -   matrix L, array[0..M-1, 0..N-1].
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixlqunpackl(ref double[,] a,
+            int m,
+            int n,
+            ref double[,] l)
+        {
+            int i = 0;
+            int k = 0;
+            int i_ = 0;
+
+            if( m<=0 | n<=0 )
+            {
+                return;
+            }
+            l = new double[m-1+1, n-1+1];
+            for(i=0; i<=n-1; i++)
+            {
+                l[0,i] = 0;
+            }
+            for(i=1; i<=m-1; i++)
+            {
+                for(i_=0; i_<=n-1;i_++)
+                {
+                    l[i,i_] = l[0,i_];
+                }
+            }
+            for(i=0; i<=m-1; i++)
+            {
+                k = Math.Min(i, n-1);
+                for(i_=0; i_<=k;i_++)
+                {
+                    l[i,i_] = a[i,i_];
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine
+        See RMatrixLQ for 0-based replacement.
+        *************************************************************************/
+        public static void lqdecomposition(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau)
+        {
+            double[] work = new double[0];
+            double[] t = new double[0];
+            int i = 0;
+            int k = 0;
+            int nmip1 = 0;
+            int minmn = 0;
+            int maxmn = 0;
+            double tmp = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            minmn = Math.Min(m, n);
+            maxmn = Math.Max(m, n);
+            work = new double[m+1];
+            t = new double[n+1];
+            tau = new double[minmn+1];
+            
+            //
+            // Test the input arguments
+            //
+            k = Math.Min(m, n);
+            for(i=1; i<=k; i++)
+            {
+                
+                //
+                // Generate elementary reflector H(i) to annihilate A(i,i+1:n)
+                //
+                nmip1 = n-i+1;
+                i1_ = (i) - (1);
+                for(i_=1; i_<=nmip1;i_++)
+                {
+                    t[i_] = a[i,i_+i1_];
+                }
+                reflections.generatereflection(ref t, nmip1, ref tmp);
+                tau[i] = tmp;
+                i1_ = (1) - (i);
+                for(i_=i; i_<=n;i_++)
+                {
+                    a[i,i_] = t[i_+i1_];
+                }
+                t[1] = 1;
+                if( i<n )
+                {
+                    
+                    //
+                    // Apply H(i) to A(i+1:m,i:n) from the right
+                    //
+                    reflections.applyreflectionfromtheright(ref a, tau[i], ref t, i+1, m, i, n, ref work);
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine
+        See RMatrixLQUnpackQ for 0-based replacement.
+        *************************************************************************/
+        public static void unpackqfromlq(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau,
+            int qrows,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int minmn = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int vm = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            System.Diagnostics.Debug.Assert(qrows<=n, "UnpackQFromLQ: QRows>N!");
+            if( m==0 | n==0 | qrows==0 )
+            {
+                return;
+            }
+            
+            //
+            // init
+            //
+            minmn = Math.Min(m, n);
+            k = Math.Min(minmn, qrows);
+            q = new double[qrows+1, n+1];
+            v = new double[n+1];
+            work = new double[qrows+1];
+            for(i=1; i<=qrows; i++)
+            {
+                for(j=1; j<=n; j++)
+                {
+                    if( i==j )
+                    {
+                        q[i,j] = 1;
+                    }
+                    else
+                    {
+                        q[i,j] = 0;
+                    }
+                }
+            }
+            
+            //
+            // unpack Q
+            //
+            for(i=k; i>=1; i--)
+            {
+                
+                //
+                // Apply H(i)
+                //
+                vm = n-i+1;
+                i1_ = (i) - (1);
+                for(i_=1; i_<=vm;i_++)
+                {
+                    v[i_] = a[i,i_+i1_];
+                }
+                v[1] = 1;
+                reflections.applyreflectionfromtheright(ref q, tau[i], ref v, 1, qrows, i, n, ref work);
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine
+        *************************************************************************/
+        public static void lqdecompositionunpacked(double[,] a,
+            int m,
+            int n,
+            ref double[,] l,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+            double[] tau = new double[0];
+
+            a = (double[,])a.Clone();
+
+            if( n<=0 )
+            {
+                return;
+            }
+            q = new double[n+1, n+1];
+            l = new double[m+1, n+1];
+            
+            //
+            // LQDecomposition
+            //
+            lqdecomposition(ref a, m, n, ref tau);
+            
+            //
+            // L
+            //
+            for(i=1; i<=m; i++)
+            {
+                for(j=1; j<=n; j++)
+                {
+                    if( j>i )
+                    {
+                        l[i,j] = 0;
+                    }
+                    else
+                    {
+                        l[i,j] = a[i,j];
+                    }
+                }
+            }
+            
+            //
+            // Q
+            //
+            unpackqfromlq(ref a, m, n, ref tau, n, ref q);
+        }
+    }
+}
Index: trunk/sources/ALGLIB/qr.cs
===================================================================
--- trunk/sources/ALGLIB/qr.cs	(revision 2430)
+++ trunk/sources/ALGLIB/qr.cs	(revision 2430)
@@ -0,0 +1,455 @@
+/*************************************************************************
+Copyright (c) 1992-2007 The University of Tennessee.  All rights reserved.
+
+Contributors:
+    * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to
+      pseudocode.
+
+See subroutines comments for additional copyrights.
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class qr
+    {
+        /*************************************************************************
+        QR decomposition of a rectangular matrix of size MxN
+
+        Input parameters:
+            A   -   matrix A whose indexes range within [0..M-1, 0..N-1].
+            M   -   number of rows in matrix A.
+            N   -   number of columns in matrix A.
+
+        Output parameters:
+            A   -   matrices Q and R in compact form (see below).
+            Tau -   array of scalar factors which are used to form
+                    matrix Q. Array whose index ranges within [0.. Min(M-1,N-1)].
+
+        Matrix A is represented as A = QR, where Q is an orthogonal matrix of size
+        MxM, R - upper triangular (or upper trapezoid) matrix of size M x N.
+
+        The elements of matrix R are located on and above the main diagonal of
+        matrix A. The elements which are located in Tau array and below the main
+        diagonal of matrix A are used to form matrix Q as follows:
+
+        Matrix Q is represented as a product of elementary reflections
+
+        Q = H(0)*H(2)*...*H(k-1),
+
+        where k = min(m,n), and each H(i) is in the form
+
+        H(i) = 1 - tau * v * (v^T)
+
+        where tau is a scalar stored in Tau[I]; v - real vector,
+        so that v(0:i-1) = 0, v(i) = 1, v(i+1:m-1) stored in A(i+1:m-1,i).
+
+          -- LAPACK routine (version 3.0) --
+             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
+             Courant Institute, Argonne National Lab, and Rice University
+             February 29, 1992.
+             Translation from FORTRAN to pseudocode (AlgoPascal)
+             by Sergey Bochkanov, ALGLIB project, 2005-2007.
+        *************************************************************************/
+        public static void rmatrixqr(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau)
+        {
+            double[] work = new double[0];
+            double[] t = new double[0];
+            int i = 0;
+            int k = 0;
+            int minmn = 0;
+            double tmp = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( m<=0 | n<=0 )
+            {
+                return;
+            }
+            minmn = Math.Min(m, n);
+            work = new double[n-1+1];
+            t = new double[m+1];
+            tau = new double[minmn-1+1];
+            
+            //
+            // Test the input arguments
+            //
+            k = minmn;
+            for(i=0; i<=k-1; i++)
+            {
+                
+                //
+                // Generate elementary reflector H(i) to annihilate A(i+1:m,i)
+                //
+                i1_ = (i) - (1);
+                for(i_=1; i_<=m-i;i_++)
+                {
+                    t[i_] = a[i_+i1_,i];
+                }
+                reflections.generatereflection(ref t, m-i, ref tmp);
+                tau[i] = tmp;
+                i1_ = (1) - (i);
+                for(i_=i; i_<=m-1;i_++)
+                {
+                    a[i_,i] = t[i_+i1_];
+                }
+                t[1] = 1;
+                if( i<n )
+                {
+                    
+                    //
+                    // Apply H(i) to A(i:m-1,i+1:n-1) from the left
+                    //
+                    reflections.applyreflectionfromtheleft(ref a, tau[i], ref t, i, m-1, i+1, n-1, ref work);
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Partial unpacking of matrix Q from the QR decomposition of a matrix A
+
+        Input parameters:
+            A       -   matrices Q and R in compact form.
+                        Output of RMatrixQR subroutine.
+            M       -   number of rows in given matrix A. M>=0.
+            N       -   number of columns in given matrix A. N>=0.
+            Tau     -   scalar factors which are used to form Q.
+                        Output of the RMatrixQR subroutine.
+            QColumns -  required number of columns of matrix Q. M>=QColumns>=0.
+
+        Output parameters:
+            Q       -   first QColumns columns of matrix Q.
+                        Array whose indexes range within [0..M-1, 0..QColumns-1].
+                        If QColumns=0, the array remains unchanged.
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixqrunpackq(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau,
+            int qcolumns,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int minmn = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int i_ = 0;
+            int i1_ = 0;
+
+            System.Diagnostics.Debug.Assert(qcolumns<=m, "UnpackQFromQR: QColumns>M!");
+            if( m<=0 | n<=0 | qcolumns<=0 )
+            {
+                return;
+            }
+            
+            //
+            // init
+            //
+            minmn = Math.Min(m, n);
+            k = Math.Min(minmn, qcolumns);
+            q = new double[m-1+1, qcolumns-1+1];
+            v = new double[m+1];
+            work = new double[qcolumns-1+1];
+            for(i=0; i<=m-1; i++)
+            {
+                for(j=0; j<=qcolumns-1; j++)
+                {
+                    if( i==j )
+                    {
+                        q[i,j] = 1;
+                    }
+                    else
+                    {
+                        q[i,j] = 0;
+                    }
+                }
+            }
+            
+            //
+            // unpack Q
+            //
+            for(i=k-1; i>=0; i--)
+            {
+                
+                //
+                // Apply H(i)
+                //
+                i1_ = (i) - (1);
+                for(i_=1; i_<=m-i;i_++)
+                {
+                    v[i_] = a[i_+i1_,i];
+                }
+                v[1] = 1;
+                reflections.applyreflectionfromtheleft(ref q, tau[i], ref v, i, m-1, 0, qcolumns-1, ref work);
+            }
+        }
+
+
+        /*************************************************************************
+        Unpacking of matrix R from the QR decomposition of a matrix A
+
+        Input parameters:
+            A       -   matrices Q and R in compact form.
+                        Output of RMatrixQR subroutine.
+            M       -   number of rows in given matrix A. M>=0.
+            N       -   number of columns in given matrix A. N>=0.
+
+        Output parameters:
+            R       -   matrix R, array[0..M-1, 0..N-1].
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static void rmatrixqrunpackr(ref double[,] a,
+            int m,
+            int n,
+            ref double[,] r)
+        {
+            int i = 0;
+            int k = 0;
+            int i_ = 0;
+
+            if( m<=0 | n<=0 )
+            {
+                return;
+            }
+            k = Math.Min(m, n);
+            r = new double[m-1+1, n-1+1];
+            for(i=0; i<=n-1; i++)
+            {
+                r[0,i] = 0;
+            }
+            for(i=1; i<=m-1; i++)
+            {
+                for(i_=0; i_<=n-1;i_++)
+                {
+                    r[i,i_] = r[0,i_];
+                }
+            }
+            for(i=0; i<=k-1; i++)
+            {
+                for(i_=i; i_<=n-1;i_++)
+                {
+                    r[i,i_] = a[i,i_];
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine. See RMatrixQR for 0-based replacement.
+        *************************************************************************/
+        public static void qrdecomposition(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau)
+        {
+            double[] work = new double[0];
+            double[] t = new double[0];
+            int i = 0;
+            int k = 0;
+            int mmip1 = 0;
+            int minmn = 0;
+            double tmp = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            minmn = Math.Min(m, n);
+            work = new double[n+1];
+            t = new double[m+1];
+            tau = new double[minmn+1];
+            
+            //
+            // Test the input arguments
+            //
+            k = Math.Min(m, n);
+            for(i=1; i<=k; i++)
+            {
+                
+                //
+                // Generate elementary reflector H(i) to annihilate A(i+1:m,i)
+                //
+                mmip1 = m-i+1;
+                i1_ = (i) - (1);
+                for(i_=1; i_<=mmip1;i_++)
+                {
+                    t[i_] = a[i_+i1_,i];
+                }
+                reflections.generatereflection(ref t, mmip1, ref tmp);
+                tau[i] = tmp;
+                i1_ = (1) - (i);
+                for(i_=i; i_<=m;i_++)
+                {
+                    a[i_,i] = t[i_+i1_];
+                }
+                t[1] = 1;
+                if( i<n )
+                {
+                    
+                    //
+                    // Apply H(i) to A(i:m,i+1:n) from the left
+                    //
+                    reflections.applyreflectionfromtheleft(ref a, tau[i], ref t, i, m, i+1, n, ref work);
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine. See RMatrixQRUnpackQ for 0-based replacement.
+        *************************************************************************/
+        public static void unpackqfromqr(ref double[,] a,
+            int m,
+            int n,
+            ref double[] tau,
+            int qcolumns,
+            ref double[,] q)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int minmn = 0;
+            double[] v = new double[0];
+            double[] work = new double[0];
+            int vm = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            System.Diagnostics.Debug.Assert(qcolumns<=m, "UnpackQFromQR: QColumns>M!");
+            if( m==0 | n==0 | qcolumns==0 )
+            {
+                return;
+            }
+            
+            //
+            // init
+            //
+            minmn = Math.Min(m, n);
+            k = Math.Min(minmn, qcolumns);
+            q = new double[m+1, qcolumns+1];
+            v = new double[m+1];
+            work = new double[qcolumns+1];
+            for(i=1; i<=m; i++)
+            {
+                for(j=1; j<=qcolumns; j++)
+                {
+                    if( i==j )
+                    {
+                        q[i,j] = 1;
+                    }
+                    else
+                    {
+                        q[i,j] = 0;
+                    }
+                }
+            }
+            
+            //
+            // unpack Q
+            //
+            for(i=k; i>=1; i--)
+            {
+                
+                //
+                // Apply H(i)
+                //
+                vm = m-i+1;
+                i1_ = (i) - (1);
+                for(i_=1; i_<=vm;i_++)
+                {
+                    v[i_] = a[i_+i1_,i];
+                }
+                v[1] = 1;
+                reflections.applyreflectionfromtheleft(ref q, tau[i], ref v, i, m, 1, qcolumns, ref work);
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine. See RMatrixQR for 0-based replacement.
+        *************************************************************************/
+        public static void qrdecompositionunpacked(double[,] a,
+            int m,
+            int n,
+            ref double[,] q,
+            ref double[,] r)
+        {
+            int i = 0;
+            int k = 0;
+            double[] tau = new double[0];
+            double[] work = new double[0];
+            double[] v = new double[0];
+            int i_ = 0;
+
+            a = (double[,])a.Clone();
+
+            k = Math.Min(m, n);
+            if( n<=0 )
+            {
+                return;
+            }
+            work = new double[m+1];
+            v = new double[m+1];
+            q = new double[m+1, m+1];
+            r = new double[m+1, n+1];
+            
+            //
+            // QRDecomposition
+            //
+            qrdecomposition(ref a, m, n, ref tau);
+            
+            //
+            // R
+            //
+            for(i=1; i<=n; i++)
+            {
+                r[1,i] = 0;
+            }
+            for(i=2; i<=m; i++)
+            {
+                for(i_=1; i_<=n;i_++)
+                {
+                    r[i,i_] = r[1,i_];
+                }
+            }
+            for(i=1; i<=k; i++)
+            {
+                for(i_=i; i_<=n;i_++)
+                {
+                    r[i,i_] = a[i,i_];
+                }
+            }
+            
+            //
+            // Q
+            //
+            unpackqfromqr(ref a, m, n, ref tau, m, ref q);
+        }
+    }
+}
Index: trunk/sources/ALGLIB/reflections.cs
===================================================================
--- trunk/sources/ALGLIB/reflections.cs	(revision 2430)
+++ trunk/sources/ALGLIB/reflections.cs	(revision 2430)
@@ -0,0 +1,330 @@
+/*************************************************************************
+Copyright (c) 1992-2007 The University of Tennessee.  All rights reserved.
+
+Contributors:
+    * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to
+      pseudocode.
+
+See subroutines comments for additional copyrights.
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class reflections
+    {
+        /*************************************************************************
+        Generation of an elementary reflection transformation
+
+        The subroutine generates elementary reflection H of order N, so that, for
+        a given X, the following equality holds true:
+
+            ( X(1) )   ( Beta )
+        H * (  ..  ) = (  0   )
+            ( X(n) )   (  0   )
+
+        where
+                      ( V(1) )
+        H = 1 - Tau * (  ..  ) * ( V(1), ..., V(n) )
+                      ( V(n) )
+
+        where the first component of vector V equals 1.
+
+        Input parameters:
+            X   -   vector. Array whose index ranges within [1..N].
+            N   -   reflection order.
+
+        Output parameters:
+            X   -   components from 2 to N are replaced with vector V.
+                    The first component is replaced with parameter Beta.
+            Tau -   scalar value Tau. If X is a null vector, Tau equals 0,
+                    otherwise 1 <= Tau <= 2.
+
+        This subroutine is the modification of the DLARFG subroutines from
+        the LAPACK library.
+
+        MODIFICATIONS:
+            24.12.2005 sign(Alpha) was replaced with an analogous to the Fortran SIGN code.
+
+          -- LAPACK auxiliary routine (version 3.0) --
+             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
+             Courant Institute, Argonne National Lab, and Rice University
+             September 30, 1994
+        *************************************************************************/
+        public static void generatereflection(ref double[] x,
+            int n,
+            ref double tau)
+        {
+            int j = 0;
+            double alpha = 0;
+            double xnorm = 0;
+            double v = 0;
+            double beta = 0;
+            double mx = 0;
+            double s = 0;
+            int i_ = 0;
+
+            if( n<=1 )
+            {
+                tau = 0;
+                return;
+            }
+            
+            //
+            // Scale if needed (to avoid overflow/underflow during intermediate
+            // calculations).
+            //
+            mx = 0;
+            for(j=1; j<=n; j++)
+            {
+                mx = Math.Max(Math.Abs(x[j]), mx);
+            }
+            s = 1;
+            if( mx!=0 )
+            {
+                if( mx<=AP.Math.MinRealNumber/AP.Math.MachineEpsilon )
+                {
+                    s = AP.Math.MinRealNumber/AP.Math.MachineEpsilon;
+                    v = 1/s;
+                    for(i_=1; i_<=n;i_++)
+                    {
+                        x[i_] = v*x[i_];
+                    }
+                    mx = mx*v;
+                }
+                else
+                {
+                    if( mx>=AP.Math.MaxRealNumber*AP.Math.MachineEpsilon )
+                    {
+                        s = AP.Math.MaxRealNumber*AP.Math.MachineEpsilon;
+                        v = 1/s;
+                        for(i_=1; i_<=n;i_++)
+                        {
+                            x[i_] = v*x[i_];
+                        }
+                        mx = mx*v;
+                    }
+                }
+            }
+            
+            //
+            // XNORM = DNRM2( N-1, X, INCX )
+            //
+            alpha = x[1];
+            xnorm = 0;
+            if( mx!=0 )
+            {
+                for(j=2; j<=n; j++)
+                {
+                    xnorm = xnorm+AP.Math.Sqr(x[j]/mx);
+                }
+                xnorm = Math.Sqrt(xnorm)*mx;
+            }
+            if( xnorm==0 )
+            {
+                
+                //
+                // H  =  I
+                //
+                tau = 0;
+                x[1] = x[1]*s;
+                return;
+            }
+            
+            //
+            // general case
+            //
+            mx = Math.Max(Math.Abs(alpha), Math.Abs(xnorm));
+            beta = -(mx*Math.Sqrt(AP.Math.Sqr(alpha/mx)+AP.Math.Sqr(xnorm/mx)));
+            if( alpha<0 )
+            {
+                beta = -beta;
+            }
+            tau = (beta-alpha)/beta;
+            v = 1/(alpha-beta);
+            for(i_=2; i_<=n;i_++)
+            {
+                x[i_] = v*x[i_];
+            }
+            x[1] = beta;
+            
+            //
+            // Scale back outputs
+            //
+            x[1] = x[1]*s;
+        }
+
+
+        /*************************************************************************
+        Application of an elementary reflection to a rectangular matrix of size MxN
+
+        The algorithm pre-multiplies the matrix by an elementary reflection transformation
+        which is given by column V and scalar Tau (see the description of the
+        GenerateReflection procedure). Not the whole matrix but only a part of it
+        is transformed (rows from M1 to M2, columns from N1 to N2). Only the elements
+        of this submatrix are changed.
+
+        Input parameters:
+            C       -   matrix to be transformed.
+            Tau     -   scalar defining the transformation.
+            V       -   column defining the transformation.
+                        Array whose index ranges within [1..M2-M1+1].
+            M1, M2  -   range of rows to be transformed.
+            N1, N2  -   range of columns to be transformed.
+            WORK    -   working array whose indexes goes from N1 to N2.
+
+        Output parameters:
+            C       -   the result of multiplying the input matrix C by the
+                        transformation matrix which is given by Tau and V.
+                        If N1>N2 or M1>M2, C is not modified.
+
+          -- LAPACK auxiliary routine (version 3.0) --
+             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
+             Courant Institute, Argonne National Lab, and Rice University
+             September 30, 1994
+        *************************************************************************/
+        public static void applyreflectionfromtheleft(ref double[,] c,
+            double tau,
+            ref double[] v,
+            int m1,
+            int m2,
+            int n1,
+            int n2,
+            ref double[] work)
+        {
+            double t = 0;
+            int i = 0;
+            int vm = 0;
+            int i_ = 0;
+
+            if( tau==0 | n1>n2 | m1>m2 )
+            {
+                return;
+            }
+            
+            //
+            // w := C' * v
+            //
+            vm = m2-m1+1;
+            for(i=n1; i<=n2; i++)
+            {
+                work[i] = 0;
+            }
+            for(i=m1; i<=m2; i++)
+            {
+                t = v[i+1-m1];
+                for(i_=n1; i_<=n2;i_++)
+                {
+                    work[i_] = work[i_] + t*c[i,i_];
+                }
+            }
+            
+            //
+            // C := C - tau * v * w'
+            //
+            for(i=m1; i<=m2; i++)
+            {
+                t = v[i-m1+1]*tau;
+                for(i_=n1; i_<=n2;i_++)
+                {
+                    c[i,i_] = c[i,i_] - t*work[i_];
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Application of an elementary reflection to a rectangular matrix of size MxN
+
+        The algorithm post-multiplies the matrix by an elementary reflection transformation
+        which is given by column V and scalar Tau (see the description of the
+        GenerateReflection procedure). Not the whole matrix but only a part of it
+        is transformed (rows from M1 to M2, columns from N1 to N2). Only the
+        elements of this submatrix are changed.
+
+        Input parameters:
+            C       -   matrix to be transformed.
+            Tau     -   scalar defining the transformation.
+            V       -   column defining the transformation.
+                        Array whose index ranges within [1..N2-N1+1].
+            M1, M2  -   range of rows to be transformed.
+            N1, N2  -   range of columns to be transformed.
+            WORK    -   working array whose indexes goes from M1 to M2.
+
+        Output parameters:
+            C       -   the result of multiplying the input matrix C by the
+                        transformation matrix which is given by Tau and V.
+                        If N1>N2 or M1>M2, C is not modified.
+
+          -- LAPACK auxiliary routine (version 3.0) --
+             Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
+             Courant Institute, Argonne National Lab, and Rice University
+             September 30, 1994
+        *************************************************************************/
+        public static void applyreflectionfromtheright(ref double[,] c,
+            double tau,
+            ref double[] v,
+            int m1,
+            int m2,
+            int n1,
+            int n2,
+            ref double[] work)
+        {
+            double t = 0;
+            int i = 0;
+            int vm = 0;
+            int i_ = 0;
+            int i1_ = 0;
+
+            if( tau==0 | n1>n2 | m1>m2 )
+            {
+                return;
+            }
+            
+            //
+            // w := C * v
+            //
+            vm = n2-n1+1;
+            for(i=m1; i<=m2; i++)
+            {
+                i1_ = (1)-(n1);
+                t = 0.0;
+                for(i_=n1; i_<=n2;i_++)
+                {
+                    t += c[i,i_]*v[i_+i1_];
+                }
+                work[i] = t;
+            }
+            
+            //
+            // C := C - w * v'
+            //
+            for(i=m1; i<=m2; i++)
+            {
+                t = work[i]*tau;
+                i1_ = (1) - (n1);
+                for(i_=n1; i_<=n2;i_++)
+                {
+                    c[i,i_] = c[i,i_] - t*v[i_+i1_];
+                }
+            }
+        }
+    }
+}
Index: trunk/sources/ALGLIB/rotations.cs
===================================================================
--- trunk/sources/ALGLIB/rotations.cs	(revision 2430)
+++ trunk/sources/ALGLIB/rotations.cs	(revision 2430)
@@ -0,0 +1,529 @@
+/*************************************************************************
+Copyright (c) 1992-2007 The University of Tennessee.  All rights reserved.
+
+Contributors:
+    * Sergey Bochkanov (ALGLIB project). Translation from FORTRAN to
+      pseudocode.
+
+See subroutines comments for additional copyrights.
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class rotations
+    {
+        /*************************************************************************
+        Application of a sequence of  elementary rotations to a matrix
+
+        The algorithm pre-multiplies the matrix by a sequence of rotation
+        transformations which is given by arrays C and S. Depending on the value
+        of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true)
+        rows are rotated, or the rows N and N-1, N-2 and N-3 and so on, are rotated.
+
+        Not the whole matrix but only a part of it is transformed (rows from M1 to
+        M2, columns from N1 to N2). Only the elements of this submatrix are changed.
+
+        Input parameters:
+            IsForward   -   the sequence of the rotation application.
+            M1,M2       -   the range of rows to be transformed.
+            N1, N2      -   the range of columns to be transformed.
+            C,S         -   transformation coefficients.
+                            Array whose index ranges within [1..M2-M1].
+            A           -   processed matrix.
+            WORK        -   working array whose index ranges within [N1..N2].
+
+        Output parameters:
+            A           -   transformed matrix.
+
+        Utility subroutine.
+        *************************************************************************/
+        public static void applyrotationsfromtheleft(bool isforward,
+            int m1,
+            int m2,
+            int n1,
+            int n2,
+            ref double[] c,
+            ref double[] s,
+            ref double[,] a,
+            ref double[] work)
+        {
+            int j = 0;
+            int jp1 = 0;
+            double ctemp = 0;
+            double stemp = 0;
+            double temp = 0;
+            int i_ = 0;
+
+            if( m1>m2 | n1>n2 )
+            {
+                return;
+            }
+            
+            //
+            // Form  P * A
+            //
+            if( isforward )
+            {
+                if( n1!=n2 )
+                {
+                    
+                    //
+                    // Common case: N1<>N2
+                    //
+                    for(j=m1; j<=m2-1; j++)
+                    {
+                        ctemp = c[j-m1+1];
+                        stemp = s[j-m1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            jp1 = j+1;
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                work[i_] = ctemp*a[jp1,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                work[i_] = work[i_] - stemp*a[j,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                a[j,i_] = ctemp*a[j,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                a[j,i_] = a[j,i_] + stemp*a[jp1,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                a[jp1,i_] = work[i_];
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    
+                    //
+                    // Special case: N1=N2
+                    //
+                    for(j=m1; j<=m2-1; j++)
+                    {
+                        ctemp = c[j-m1+1];
+                        stemp = s[j-m1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            temp = a[j+1,n1];
+                            a[j+1,n1] = ctemp*temp-stemp*a[j,n1];
+                            a[j,n1] = stemp*temp+ctemp*a[j,n1];
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if( n1!=n2 )
+                {
+                    
+                    //
+                    // Common case: N1<>N2
+                    //
+                    for(j=m2-1; j>=m1; j--)
+                    {
+                        ctemp = c[j-m1+1];
+                        stemp = s[j-m1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            jp1 = j+1;
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                work[i_] = ctemp*a[jp1,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                work[i_] = work[i_] - stemp*a[j,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                a[j,i_] = ctemp*a[j,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                a[j,i_] = a[j,i_] + stemp*a[jp1,i_];
+                            }
+                            for(i_=n1; i_<=n2;i_++)
+                            {
+                                a[jp1,i_] = work[i_];
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    
+                    //
+                    // Special case: N1=N2
+                    //
+                    for(j=m2-1; j>=m1; j--)
+                    {
+                        ctemp = c[j-m1+1];
+                        stemp = s[j-m1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            temp = a[j+1,n1];
+                            a[j+1,n1] = ctemp*temp-stemp*a[j,n1];
+                            a[j,n1] = stemp*temp+ctemp*a[j,n1];
+                        }
+                    }
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Application of a sequence of  elementary rotations to a matrix
+
+        The algorithm post-multiplies the matrix by a sequence of rotation
+        transformations which is given by arrays C and S. Depending on the value
+        of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true)
+        rows are rotated, or the rows N and N-1, N-2 and N-3 and so on are rotated.
+
+        Not the whole matrix but only a part of it is transformed (rows from M1
+        to M2, columns from N1 to N2). Only the elements of this submatrix are changed.
+
+        Input parameters:
+            IsForward   -   the sequence of the rotation application.
+            M1,M2       -   the range of rows to be transformed.
+            N1, N2      -   the range of columns to be transformed.
+            C,S         -   transformation coefficients.
+                            Array whose index ranges within [1..N2-N1].
+            A           -   processed matrix.
+            WORK        -   working array whose index ranges within [M1..M2].
+
+        Output parameters:
+            A           -   transformed matrix.
+
+        Utility subroutine.
+        *************************************************************************/
+        public static void applyrotationsfromtheright(bool isforward,
+            int m1,
+            int m2,
+            int n1,
+            int n2,
+            ref double[] c,
+            ref double[] s,
+            ref double[,] a,
+            ref double[] work)
+        {
+            int j = 0;
+            int jp1 = 0;
+            double ctemp = 0;
+            double stemp = 0;
+            double temp = 0;
+            int i_ = 0;
+
+            
+            //
+            // Form A * P'
+            //
+            if( isforward )
+            {
+                if( m1!=m2 )
+                {
+                    
+                    //
+                    // Common case: M1<>M2
+                    //
+                    for(j=n1; j<=n2-1; j++)
+                    {
+                        ctemp = c[j-n1+1];
+                        stemp = s[j-n1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            jp1 = j+1;
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                work[i_] = ctemp*a[i_,jp1];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                work[i_] = work[i_] - stemp*a[i_,j];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                a[i_,j] = ctemp*a[i_,j];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                a[i_,j] = a[i_,j] + stemp*a[i_,jp1];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                a[i_,jp1] = work[i_];
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    
+                    //
+                    // Special case: M1=M2
+                    //
+                    for(j=n1; j<=n2-1; j++)
+                    {
+                        ctemp = c[j-n1+1];
+                        stemp = s[j-n1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            temp = a[m1,j+1];
+                            a[m1,j+1] = ctemp*temp-stemp*a[m1,j];
+                            a[m1,j] = stemp*temp+ctemp*a[m1,j];
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if( m1!=m2 )
+                {
+                    
+                    //
+                    // Common case: M1<>M2
+                    //
+                    for(j=n2-1; j>=n1; j--)
+                    {
+                        ctemp = c[j-n1+1];
+                        stemp = s[j-n1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            jp1 = j+1;
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                work[i_] = ctemp*a[i_,jp1];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                work[i_] = work[i_] - stemp*a[i_,j];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                a[i_,j] = ctemp*a[i_,j];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                a[i_,j] = a[i_,j] + stemp*a[i_,jp1];
+                            }
+                            for(i_=m1; i_<=m2;i_++)
+                            {
+                                a[i_,jp1] = work[i_];
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    
+                    //
+                    // Special case: M1=M2
+                    //
+                    for(j=n2-1; j>=n1; j--)
+                    {
+                        ctemp = c[j-n1+1];
+                        stemp = s[j-n1+1];
+                        if( ctemp!=1 | stemp!=0 )
+                        {
+                            temp = a[m1,j+1];
+                            a[m1,j+1] = ctemp*temp-stemp*a[m1,j];
+                            a[m1,j] = stemp*temp+ctemp*a[m1,j];
+                        }
+                    }
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        The subroutine generates the elementary rotation, so that:
+
+        [  CS  SN  ]  .  [ F ]  =  [ R ]
+        [ -SN  CS  ]     [ G ]     [ 0 ]
+
+        CS**2 + SN**2 = 1
+        *************************************************************************/
+        public static void generaterotation(double f,
+            double g,
+            ref double cs,
+            ref double sn,
+            ref double r)
+        {
+            double f1 = 0;
+            double g1 = 0;
+
+            if( g==0 )
+            {
+                cs = 1;
+                sn = 0;
+                r = f;
+            }
+            else
+            {
+                if( f==0 )
+                {
+                    cs = 0;
+                    sn = 1;
+                    r = g;
+                }
+                else
+                {
+                    f1 = f;
+                    g1 = g;
+                    r = Math.Sqrt(AP.Math.Sqr(f1)+AP.Math.Sqr(g1));
+                    cs = f1/r;
+                    sn = g1/r;
+                    if( Math.Abs(f)>Math.Abs(g) & cs<0 )
+                    {
+                        cs = -cs;
+                        sn = -sn;
+                        r = -r;
+                    }
+                }
+            }
+        }
+
+
+        private static void testrotations()
+        {
+            double[,] al1 = new double[0,0];
+            double[,] al2 = new double[0,0];
+            double[,] ar1 = new double[0,0];
+            double[,] ar2 = new double[0,0];
+            double[] cl = new double[0];
+            double[] sl = new double[0];
+            double[] cr = new double[0];
+            double[] sr = new double[0];
+            double[] w = new double[0];
+            int m = 0;
+            int n = 0;
+            int maxmn = 0;
+            double t = 0;
+            int pass = 0;
+            int passcount = 0;
+            int i = 0;
+            int j = 0;
+            double err = 0;
+            double maxerr = 0;
+            bool isforward = new bool();
+
+            passcount = 1000;
+            maxerr = 0;
+            for(pass=1; pass<=passcount; pass++)
+            {
+                
+                //
+                // settings
+                //
+                m = 2+AP.Math.RandomInteger(50);
+                n = 2+AP.Math.RandomInteger(50);
+                isforward = AP.Math.RandomReal()>0.5;
+                maxmn = Math.Max(m, n);
+                al1 = new double[m+1, n+1];
+                al2 = new double[m+1, n+1];
+                ar1 = new double[m+1, n+1];
+                ar2 = new double[m+1, n+1];
+                cl = new double[m-1+1];
+                sl = new double[m-1+1];
+                cr = new double[n-1+1];
+                sr = new double[n-1+1];
+                w = new double[maxmn+1];
+                
+                //
+                // matrices and rotaions
+                //
+                for(i=1; i<=m; i++)
+                {
+                    for(j=1; j<=n; j++)
+                    {
+                        al1[i,j] = 2*AP.Math.RandomReal()-1;
+                        al2[i,j] = al1[i,j];
+                        ar1[i,j] = al1[i,j];
+                        ar2[i,j] = al1[i,j];
+                    }
+                }
+                for(i=1; i<=m-1; i++)
+                {
+                    t = 2*Math.PI*AP.Math.RandomReal();
+                    cl[i] = Math.Cos(t);
+                    sl[i] = Math.Sin(t);
+                }
+                for(j=1; j<=n-1; j++)
+                {
+                    t = 2*Math.PI*AP.Math.RandomReal();
+                    cr[j] = Math.Cos(t);
+                    sr[j] = Math.Sin(t);
+                }
+                
+                //
+                // Test left
+                //
+                applyrotationsfromtheleft(isforward, 1, m, 1, n, ref cl, ref sl, ref al1, ref w);
+                for(j=1; j<=n; j++)
+                {
+                    applyrotationsfromtheleft(isforward, 1, m, j, j, ref cl, ref sl, ref al2, ref w);
+                }
+                err = 0;
+                for(i=1; i<=m; i++)
+                {
+                    for(j=1; j<=n; j++)
+                    {
+                        err = Math.Max(err, Math.Abs(al1[i,j]-al2[i,j]));
+                    }
+                }
+                maxerr = Math.Max(err, maxerr);
+                
+                //
+                // Test right
+                //
+                applyrotationsfromtheright(isforward, 1, m, 1, n, ref cr, ref sr, ref ar1, ref w);
+                for(i=1; i<=m; i++)
+                {
+                    applyrotationsfromtheright(isforward, i, i, 1, n, ref cr, ref sr, ref ar2, ref w);
+                }
+                err = 0;
+                for(i=1; i<=m; i++)
+                {
+                    for(j=1; j<=n; j++)
+                    {
+                        err = Math.Max(err, Math.Abs(ar1[i,j]-ar2[i,j]));
+                    }
+                }
+                maxerr = Math.Max(err, maxerr);
+            }
+            System.Console.Write("TESTING ROTATIONS");
+            System.Console.WriteLine();
+            System.Console.Write("Pass count ");
+            System.Console.Write("{0,0:d}",passcount);
+            System.Console.WriteLine();
+            System.Console.Write("Error is ");
+            System.Console.Write("{0,5:E3}",maxerr);
+            System.Console.WriteLine();
+        }
+    }
+}
Index: trunk/sources/ALGLIB/spline3.cs
===================================================================
--- trunk/sources/ALGLIB/spline3.cs	(revision 2430)
+++ trunk/sources/ALGLIB/spline3.cs	(revision 2430)
@@ -0,0 +1,1273 @@
+/*************************************************************************
+Copyright (c) 2007, Sergey Bochkanov (ALGLIB project).
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class spline3
+    {
+        /*************************************************************************
+        This subroutine builds linear spline coefficients table.
+
+        Input parameters:
+            X   -   spline nodes, array[0..N-1]
+            Y   -   function values, array[0..N-1]
+            N   -   points count, N>=2
+            
+        Output parameters:
+            C   -   coefficients table.  Used  by  SplineInterpolation  and  other
+                    subroutines from this file.
+
+          -- ALGLIB PROJECT --
+             Copyright 24.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildlinearspline(double[] x,
+            double[] y,
+            int n,
+            ref double[] c)
+        {
+            int i = 0;
+            int tblsize = 0;
+
+            x = (double[])x.Clone();
+            y = (double[])y.Clone();
+
+            System.Diagnostics.Debug.Assert(n>=2, "BuildLinearSpline: N<2!");
+            
+            //
+            // Sort points
+            //
+            heapsortpoints(ref x, ref y, n);
+            
+            //
+            // Fill C:
+            //  C[0]            -   length(C)
+            //  C[1]            -   type(C):
+            //                      3 - general cubic spline
+            //  C[2]            -   N
+            //  C[3]...C[3+N-1] -   x[i], i = 0...N-1
+            //  C[3+N]...C[3+N+(N-1)*4-1] - coefficients table
+            //
+            tblsize = 3+n+(n-1)*4;
+            c = new double[tblsize-1+1];
+            c[0] = tblsize;
+            c[1] = 3;
+            c[2] = n;
+            for(i=0; i<=n-1; i++)
+            {
+                c[3+i] = x[i];
+            }
+            for(i=0; i<=n-2; i++)
+            {
+                c[3+n+4*i+0] = y[i];
+                c[3+n+4*i+1] = (y[i+1]-y[i])/(x[i+1]-x[i]);
+                c[3+n+4*i+2] = 0;
+                c[3+n+4*i+3] = 0;
+            }
+        }
+
+
+        /*************************************************************************
+        This subroutine builds cubic spline coefficients table.
+
+        Input parameters:
+            X           -   spline nodes, array[0..N-1]
+            Y           -   function values, array[0..N-1]
+            N           -   points count, N>=2
+            BoundLType  -   boundary condition type for the left boundary
+            BoundL      -   left boundary condition (first or second derivative,
+                            depending on the BoundLType)
+            BoundRType  -   boundary condition type for the right boundary
+            BoundR      -   right boundary condition (first or second derivative,
+                            depending on the BoundRType)
+
+        Output parameters:
+            C           -   coefficients table.  Used  by  SplineInterpolation and
+                            other subroutines from this file.
+                            
+        The BoundLType/BoundRType parameters can have the following values:
+            * 0,   which  corresponds  to  the  parabolically   terminated  spline
+              (BoundL/BoundR are ignored).
+            * 1, which corresponds to the first derivative boundary condition
+            * 2, which corresponds to the second derivative boundary condition
+
+          -- ALGLIB PROJECT --
+             Copyright 23.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildcubicspline(double[] x,
+            double[] y,
+            int n,
+            int boundltype,
+            double boundl,
+            int boundrtype,
+            double boundr,
+            ref double[] c)
+        {
+            double[] a1 = new double[0];
+            double[] a2 = new double[0];
+            double[] a3 = new double[0];
+            double[] b = new double[0];
+            double[] d = new double[0];
+            int i = 0;
+            int tblsize = 0;
+            double delta = 0;
+            double delta2 = 0;
+            double delta3 = 0;
+
+            x = (double[])x.Clone();
+            y = (double[])y.Clone();
+
+            System.Diagnostics.Debug.Assert(n>=2, "BuildCubicSpline: N<2!");
+            System.Diagnostics.Debug.Assert(boundltype==0 | boundltype==1 | boundltype==2, "BuildCubicSpline: incorrect BoundLType!");
+            System.Diagnostics.Debug.Assert(boundrtype==0 | boundrtype==1 | boundrtype==2, "BuildCubicSpline: incorrect BoundRType!");
+            a1 = new double[n-1+1];
+            a2 = new double[n-1+1];
+            a3 = new double[n-1+1];
+            b = new double[n-1+1];
+            
+            //
+            // Special case:
+            // * N=2
+            // * parabolic terminated boundary condition on both ends
+            //
+            if( n==2 & boundltype==0 & boundrtype==0 )
+            {
+                
+                //
+                // Change task type
+                //
+                boundltype = 2;
+                boundl = 0;
+                boundrtype = 2;
+                boundr = 0;
+            }
+            
+            //
+            //
+            // Sort points
+            //
+            heapsortpoints(ref x, ref y, n);
+            
+            //
+            // Left boundary conditions
+            //
+            if( boundltype==0 )
+            {
+                a1[0] = 0;
+                a2[0] = 1;
+                a3[0] = 1;
+                b[0] = 2*(y[1]-y[0])/(x[1]-x[0]);
+            }
+            if( boundltype==1 )
+            {
+                a1[0] = 0;
+                a2[0] = 1;
+                a3[0] = 0;
+                b[0] = boundl;
+            }
+            if( boundltype==2 )
+            {
+                a1[0] = 0;
+                a2[0] = 2;
+                a3[0] = 1;
+                b[0] = 3*(y[1]-y[0])/(x[1]-x[0])-0.5*boundl*(x[1]-x[0]);
+            }
+            
+            //
+            // Central conditions
+            //
+            for(i=1; i<=n-2; i++)
+            {
+                a1[i] = x[i+1]-x[i];
+                a2[i] = 2*(x[i+1]-x[i-1]);
+                a3[i] = x[i]-x[i-1];
+                b[i] = 3*(y[i]-y[i-1])/(x[i]-x[i-1])*(x[i+1]-x[i])+3*(y[i+1]-y[i])/(x[i+1]-x[i])*(x[i]-x[i-1]);
+            }
+            
+            //
+            // Right boundary conditions
+            //
+            if( boundrtype==0 )
+            {
+                a1[n-1] = 1;
+                a2[n-1] = 1;
+                a3[n-1] = 0;
+                b[n-1] = 2*(y[n-1]-y[n-2])/(x[n-1]-x[n-2]);
+            }
+            if( boundrtype==1 )
+            {
+                a1[n-1] = 0;
+                a2[n-1] = 1;
+                a3[n-1] = 0;
+                b[n-1] = boundr;
+            }
+            if( boundrtype==2 )
+            {
+                a1[n-1] = 1;
+                a2[n-1] = 2;
+                a3[n-1] = 0;
+                b[n-1] = 3*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])+0.5*boundr*(x[n-1]-x[n-2]);
+            }
+            
+            //
+            // Solve
+            //
+            solvetridiagonal(a1, a2, a3, b, n, ref d);
+            
+            //
+            // Now problem is reduced to the cubic Hermite spline
+            //
+            buildhermitespline(x, y, d, n, ref c);
+        }
+
+
+        /*************************************************************************
+        This subroutine builds cubic Hermite spline coefficients table.
+
+        Input parameters:
+            X           -   spline nodes, array[0..N-1]
+            Y           -   function values, array[0..N-1]
+            D           -   derivatives, array[0..N-1]
+            N           -   points count, N>=2
+
+        Output parameters:
+            C           -   coefficients table.  Used  by  SplineInterpolation and
+                            other subroutines from this file.
+
+          -- ALGLIB PROJECT --
+             Copyright 23.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildhermitespline(double[] x,
+            double[] y,
+            double[] d,
+            int n,
+            ref double[] c)
+        {
+            int i = 0;
+            int tblsize = 0;
+            double delta = 0;
+            double delta2 = 0;
+            double delta3 = 0;
+
+            x = (double[])x.Clone();
+            y = (double[])y.Clone();
+            d = (double[])d.Clone();
+
+            System.Diagnostics.Debug.Assert(n>=2, "BuildHermiteSpline: N<2!");
+            
+            //
+            // Sort points
+            //
+            heapsortdpoints(ref x, ref y, ref d, n);
+            
+            //
+            // Fill C:
+            //  C[0]            -   length(C)
+            //  C[1]            -   type(C):
+            //                      3 - general cubic spline
+            //  C[2]            -   N
+            //  C[3]...C[3+N-1] -   x[i], i = 0...N-1
+            //  C[3+N]...C[3+N+(N-1)*4-1] - coefficients table
+            //
+            tblsize = 3+n+(n-1)*4;
+            c = new double[tblsize-1+1];
+            c[0] = tblsize;
+            c[1] = 3;
+            c[2] = n;
+            for(i=0; i<=n-1; i++)
+            {
+                c[3+i] = x[i];
+            }
+            for(i=0; i<=n-2; i++)
+            {
+                delta = x[i+1]-x[i];
+                delta2 = AP.Math.Sqr(delta);
+                delta3 = delta*delta2;
+                c[3+n+4*i+0] = y[i];
+                c[3+n+4*i+1] = d[i];
+                c[3+n+4*i+2] = (3*(y[i+1]-y[i])-2*d[i]*delta-d[i+1]*delta)/delta2;
+                c[3+n+4*i+3] = (2*(y[i]-y[i+1])+d[i]*delta+d[i+1]*delta)/delta3;
+            }
+        }
+
+
+        /*************************************************************************
+        This subroutine builds Akima spline coefficients table.
+
+        Input parameters:
+            X           -   spline nodes, array[0..N-1]
+            Y           -   function values, array[0..N-1]
+            N           -   points count, N>=5
+
+        Output parameters:
+            C           -   coefficients table.  Used  by  SplineInterpolation and
+                            other subroutines from this file.
+
+          -- ALGLIB PROJECT --
+             Copyright 24.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void buildakimaspline(double[] x,
+            double[] y,
+            int n,
+            ref double[] c)
+        {
+            int i = 0;
+            double[] d = new double[0];
+            double[] w = new double[0];
+            double[] diff = new double[0];
+
+            x = (double[])x.Clone();
+            y = (double[])y.Clone();
+
+            System.Diagnostics.Debug.Assert(n>=5, "BuildAkimaSpline: N<5!");
+            
+            //
+            // Sort points
+            //
+            heapsortpoints(ref x, ref y, n);
+            
+            //
+            // Prepare W (weights), Diff (divided differences)
+            //
+            w = new double[n-2+1];
+            diff = new double[n-2+1];
+            for(i=0; i<=n-2; i++)
+            {
+                diff[i] = (y[i+1]-y[i])/(x[i+1]-x[i]);
+            }
+            for(i=1; i<=n-2; i++)
+            {
+                w[i] = Math.Abs(diff[i]-diff[i-1]);
+            }
+            
+            //
+            // Prepare Hermite interpolation scheme
+            //
+            d = new double[n-1+1];
+            for(i=2; i<=n-3; i++)
+            {
+                if( Math.Abs(w[i-1])+Math.Abs(w[i+1])!=0 )
+                {
+                    d[i] = (w[i+1]*diff[i-1]+w[i-1]*diff[i])/(w[i+1]+w[i-1]);
+                }
+                else
+                {
+                    d[i] = ((x[i+1]-x[i])*diff[i-1]+(x[i]-x[i-1])*diff[i])/(x[i+1]-x[i-1]);
+                }
+            }
+            d[0] = diffthreepoint(x[0], x[0], y[0], x[1], y[1], x[2], y[2]);
+            d[1] = diffthreepoint(x[1], x[0], y[0], x[1], y[1], x[2], y[2]);
+            d[n-2] = diffthreepoint(x[n-2], x[n-3], y[n-3], x[n-2], y[n-2], x[n-1], y[n-1]);
+            d[n-1] = diffthreepoint(x[n-1], x[n-3], y[n-3], x[n-2], y[n-2], x[n-1], y[n-1]);
+            
+            //
+            // Build Akima spline using Hermite interpolation scheme
+            //
+            buildhermitespline(x, y, d, n, ref c);
+        }
+
+
+        /*************************************************************************
+        This subroutine calculates the value of the spline at the given point X.
+
+        Input parameters:
+            C           -   coefficients table. Built by BuildLinearSpline,
+                            BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+            X           -   point
+
+        Result:
+            S(x)
+
+          -- ALGLIB PROJECT --
+             Copyright 23.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static double splineinterpolation(ref double[] c,
+            double x)
+        {
+            double result = 0;
+            int n = 0;
+            int l = 0;
+            int r = 0;
+            int m = 0;
+
+            System.Diagnostics.Debug.Assert((int)Math.Round(c[1])==3, "SplineInterpolation: incorrect C!");
+            n = (int)Math.Round(c[2]);
+            
+            //
+            // Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
+            //
+            l = 3;
+            r = 3+n-2+1;
+            while( l!=r-1 )
+            {
+                m = (l+r)/2;
+                if( c[m]>=x )
+                {
+                    r = m;
+                }
+                else
+                {
+                    l = m;
+                }
+            }
+            
+            //
+            // Interpolation
+            //
+            x = x-c[l];
+            m = 3+n+4*(l-3);
+            result = c[m]+x*(c[m+1]+x*(c[m+2]+x*c[m+3]));
+            return result;
+        }
+
+
+        /*************************************************************************
+        This subroutine differentiates the spline.
+
+        Input parameters:
+            C   -   coefficients table. Built by BuildLinearSpline,
+                    BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+            X   -   point
+
+        Result:
+            S   -   S(x)
+            DS  -   S'(x)
+            D2S -   S''(x)
+
+          -- ALGLIB PROJECT --
+             Copyright 24.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void splinedifferentiation(ref double[] c,
+            double x,
+            ref double s,
+            ref double ds,
+            ref double d2s)
+        {
+            int n = 0;
+            int l = 0;
+            int r = 0;
+            int m = 0;
+
+            System.Diagnostics.Debug.Assert((int)Math.Round(c[1])==3, "SplineInterpolation: incorrect C!");
+            n = (int)Math.Round(c[2]);
+            
+            //
+            // Binary search
+            //
+            l = 3;
+            r = 3+n-2+1;
+            while( l!=r-1 )
+            {
+                m = (l+r)/2;
+                if( c[m]>=x )
+                {
+                    r = m;
+                }
+                else
+                {
+                    l = m;
+                }
+            }
+            
+            //
+            // Differentiation
+            //
+            x = x-c[l];
+            m = 3+n+4*(l-3);
+            s = c[m]+x*(c[m+1]+x*(c[m+2]+x*c[m+3]));
+            ds = c[m+1]+2*x*c[m+2]+3*AP.Math.Sqr(x)*c[m+3];
+            d2s = 2*c[m+2]+6*x*c[m+3];
+        }
+
+
+        /*************************************************************************
+        This subroutine makes the copy of the spline.
+
+        Input parameters:
+            C   -   coefficients table. Built by BuildLinearSpline,
+                    BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+
+        Result:
+            CC  -   spline copy
+
+          -- ALGLIB PROJECT --
+             Copyright 29.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void splinecopy(ref double[] c,
+            ref double[] cc)
+        {
+            int s = 0;
+            int i_ = 0;
+
+            s = (int)Math.Round(c[0]);
+            cc = new double[s-1+1];
+            for(i_=0; i_<=s-1;i_++)
+            {
+                cc[i_] = c[i_];
+            }
+        }
+
+
+        /*************************************************************************
+        This subroutine unpacks the spline into the coefficients table.
+
+        Input parameters:
+            C   -   coefficients table. Built by BuildLinearSpline,
+                    BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+            X   -   point
+
+        Result:
+            Tbl -   coefficients table, unpacked format, array[0..N-2, 0..5].
+                    For I = 0...N-2:
+                        Tbl[I,0] = X[i]
+                        Tbl[I,1] = X[i+1]
+                        Tbl[I,2] = C0
+                        Tbl[I,3] = C1
+                        Tbl[I,4] = C2
+                        Tbl[I,5] = C3
+                    On [x[i], x[i+1]] spline is equals to:
+                        S(x) = C0 + C1*t + C2*t^2 + C3*t^3
+                        t = x-x[i]
+
+          -- ALGLIB PROJECT --
+             Copyright 29.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void splineunpack(ref double[] c,
+            ref int n,
+            ref double[,] tbl)
+        {
+            int i = 0;
+
+            System.Diagnostics.Debug.Assert((int)Math.Round(c[1])==3, "SplineUnpack: incorrect C!");
+            n = (int)Math.Round(c[2]);
+            tbl = new double[n-2+1, 5+1];
+            
+            //
+            // Fill
+            //
+            for(i=0; i<=n-2; i++)
+            {
+                tbl[i,0] = c[3+i];
+                tbl[i,1] = c[3+i+1];
+                tbl[i,2] = c[3+n+4*i];
+                tbl[i,3] = c[3+n+4*i+1];
+                tbl[i,4] = c[3+n+4*i+2];
+                tbl[i,5] = c[3+n+4*i+3];
+            }
+        }
+
+
+        /*************************************************************************
+        This subroutine performs linear transformation of the spline argument.
+
+        Input parameters:
+            C   -   coefficients table. Built by BuildLinearSpline,
+                    BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+            A, B-   transformation coefficients: x = A*t + B
+        Result:
+            C   -   transformed spline
+
+          -- ALGLIB PROJECT --
+             Copyright 30.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void splinelintransx(ref double[] c,
+            double a,
+            double b)
+        {
+            int i = 0;
+            int n = 0;
+            double v = 0;
+            double dv = 0;
+            double d2v = 0;
+            double[] x = new double[0];
+            double[] y = new double[0];
+            double[] d = new double[0];
+
+            System.Diagnostics.Debug.Assert((int)Math.Round(c[1])==3, "SplineLinTransX: incorrect C!");
+            n = (int)Math.Round(c[2]);
+            
+            //
+            // Special case: A=0
+            //
+            if( a==0 )
+            {
+                v = splineinterpolation(ref c, b);
+                for(i=0; i<=n-2; i++)
+                {
+                    c[3+n+4*i] = v;
+                    c[3+n+4*i+1] = 0;
+                    c[3+n+4*i+2] = 0;
+                    c[3+n+4*i+3] = 0;
+                }
+                return;
+            }
+            
+            //
+            // General case: A<>0.
+            // Unpack, X, Y, dY/dX.
+            // Scale and pack again.
+            //
+            x = new double[n-1+1];
+            y = new double[n-1+1];
+            d = new double[n-1+1];
+            for(i=0; i<=n-1; i++)
+            {
+                x[i] = c[3+i];
+                splinedifferentiation(ref c, x[i], ref v, ref dv, ref d2v);
+                x[i] = (x[i]-b)/a;
+                y[i] = v;
+                d[i] = a*dv;
+            }
+            buildhermitespline(x, y, d, n, ref c);
+        }
+
+
+        /*************************************************************************
+        This subroutine performs linear transformation of the spline.
+
+        Input parameters:
+            C   -   coefficients table. Built by BuildLinearSpline,
+                    BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+            A, B-   transformation coefficients: S2(x) = A*S(x) + B
+        Result:
+            C   -   transformed spline
+
+          -- ALGLIB PROJECT --
+             Copyright 30.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static void splinelintransy(ref double[] c,
+            double a,
+            double b)
+        {
+            int i = 0;
+            int n = 0;
+            double v = 0;
+            double dv = 0;
+            double d2v = 0;
+            double[] x = new double[0];
+            double[] y = new double[0];
+            double[] d = new double[0];
+
+            System.Diagnostics.Debug.Assert((int)Math.Round(c[1])==3, "SplineLinTransX: incorrect C!");
+            n = (int)Math.Round(c[2]);
+            
+            //
+            // Special case: A=0
+            //
+            for(i=0; i<=n-2; i++)
+            {
+                c[3+n+4*i] = a*c[3+n+4*i]+b;
+                c[3+n+4*i+1] = a*c[3+n+4*i+1];
+                c[3+n+4*i+2] = a*c[3+n+4*i+2];
+                c[3+n+4*i+3] = a*c[3+n+4*i+3];
+            }
+        }
+
+
+        /*************************************************************************
+        This subroutine integrates the spline.
+
+        Input parameters:
+            C   -   coefficients table. Built by BuildLinearSpline,
+                    BuildHermiteSpline, BuildCubicSpline, BuildAkimaSpline.
+            X   -   right bound of the integration interval [a, x]
+        Result:
+            integral(S(t)dt,a,x)
+
+          -- ALGLIB PROJECT --
+             Copyright 23.06.2007 by Bochkanov Sergey
+        *************************************************************************/
+        public static double splineintegration(ref double[] c,
+            double x)
+        {
+            double result = 0;
+            int n = 0;
+            int i = 0;
+            int l = 0;
+            int r = 0;
+            int m = 0;
+            double w = 0;
+
+            System.Diagnostics.Debug.Assert((int)Math.Round(c[1])==3, "SplineIntegration: incorrect C!");
+            n = (int)Math.Round(c[2]);
+            
+            //
+            // Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
+            //
+            l = 3;
+            r = 3+n-2+1;
+            while( l!=r-1 )
+            {
+                m = (l+r)/2;
+                if( c[m]>=x )
+                {
+                    r = m;
+                }
+                else
+                {
+                    l = m;
+                }
+            }
+            
+            //
+            // Integration
+            //
+            result = 0;
+            for(i=3; i<=l-1; i++)
+            {
+                w = c[i+1]-c[i];
+                m = 3+n+4*(i-3);
+                result = result+c[m]*w;
+                result = result+c[m+1]*AP.Math.Sqr(w)/2;
+                result = result+c[m+2]*AP.Math.Sqr(w)*w/3;
+                result = result+c[m+3]*AP.Math.Sqr(AP.Math.Sqr(w))/4;
+            }
+            w = x-c[l];
+            m = 3+n+4*(l-3);
+            result = result+c[m]*w;
+            result = result+c[m+1]*AP.Math.Sqr(w)/2;
+            result = result+c[m+2]*AP.Math.Sqr(w)*w/3;
+            result = result+c[m+3]*AP.Math.Sqr(AP.Math.Sqr(w))/4;
+            return result;
+        }
+
+
+        /*************************************************************************
+        Obsolete subroutine, left for backward compatibility.
+        *************************************************************************/
+        public static void spline3buildtable(int n,
+            int diffn,
+            double[] x,
+            double[] y,
+            double boundl,
+            double boundr,
+            ref double[,] ctbl)
+        {
+            bool c = new bool();
+            int e = 0;
+            int g = 0;
+            double tmp = 0;
+            int nxm1 = 0;
+            int i = 0;
+            int j = 0;
+            double dx = 0;
+            double dxj = 0;
+            double dyj = 0;
+            double dxjp1 = 0;
+            double dyjp1 = 0;
+            double dxp = 0;
+            double dyp = 0;
+            double yppa = 0;
+            double yppb = 0;
+            double pj = 0;
+            double b1 = 0;
+            double b2 = 0;
+            double b3 = 0;
+            double b4 = 0;
+
+            x = (double[])x.Clone();
+            y = (double[])y.Clone();
+
+            n = n-1;
+            g = (n+1)/2;
+            do
+            {
+                i = g;
+                do
+                {
+                    j = i-g;
+                    c = true;
+                    do
+                    {
+                        if( x[j]<=x[j+g] )
+                        {
+                            c = false;
+                        }
+                        else
+                        {
+                            tmp = x[j];
+                            x[j] = x[j+g];
+                            x[j+g] = tmp;
+                            tmp = y[j];
+                            y[j] = y[j+g];
+                            y[j+g] = tmp;
+                        }
+                        j = j-1;
+                    }
+                    while( j>=0 & c );
+                    i = i+1;
+                }
+                while( i<=n );
+                g = g/2;
+            }
+            while( g>0 );
+            ctbl = new double[4+1, n+1];
+            n = n+1;
+            if( diffn==1 )
+            {
+                b1 = 1;
+                b2 = 6/(x[1]-x[0])*((y[1]-y[0])/(x[1]-x[0])-boundl);
+                b3 = 1;
+                b4 = 6/(x[n-1]-x[n-2])*(boundr-(y[n-1]-y[n-2])/(x[n-1]-x[n-2]));
+            }
+            else
+            {
+                b1 = 0;
+                b2 = 2*boundl;
+                b3 = 0;
+                b4 = 2*boundr;
+            }
+            nxm1 = n-1;
+            if( n>=2 )
+            {
+                if( n>2 )
+                {
+                    dxj = x[1]-x[0];
+                    dyj = y[1]-y[0];
+                    j = 2;
+                    while( j<=nxm1 )
+                    {
+                        dxjp1 = x[j]-x[j-1];
+                        dyjp1 = y[j]-y[j-1];
+                        dxp = dxj+dxjp1;
+                        ctbl[1,j-1] = dxjp1/dxp;
+                        ctbl[2,j-1] = 1-ctbl[1,j-1];
+                        ctbl[3,j-1] = 6*(dyjp1/dxjp1-dyj/dxj)/dxp;
+                        dxj = dxjp1;
+                        dyj = dyjp1;
+                        j = j+1;
+                    }
+                }
+                ctbl[1,0] = -(b1/2);
+                ctbl[2,0] = b2/2;
+                if( n!=2 )
+                {
+                    j = 2;
+                    while( j<=nxm1 )
+                    {
+                        pj = ctbl[2,j-1]*ctbl[1,j-2]+2;
+                        ctbl[1,j-1] = -(ctbl[1,j-1]/pj);
+                        ctbl[2,j-1] = (ctbl[3,j-1]-ctbl[2,j-1]*ctbl[2,j-2])/pj;
+                        j = j+1;
+                    }
+                }
+                yppb = (b4-b3*ctbl[2,nxm1-1])/(b3*ctbl[1,nxm1-1]+2);
+                i = 1;
+                while( i<=nxm1 )
+                {
+                    j = n-i;
+                    yppa = ctbl[1,j-1]*yppb+ctbl[2,j-1];
+                    dx = x[j]-x[j-1];
+                    ctbl[3,j-1] = (yppb-yppa)/dx/6;
+                    ctbl[2,j-1] = yppa/2;
+                    ctbl[1,j-1] = (y[j]-y[j-1])/dx-(ctbl[2,j-1]+ctbl[3,j-1]*dx)*dx;
+                    yppb = yppa;
+                    i = i+1;
+                }
+                for(i=1; i<=n; i++)
+                {
+                    ctbl[0,i-1] = y[i-1];
+                    ctbl[4,i-1] = x[i-1];
+                }
+            }
+        }
+
+
+        /*************************************************************************
+        Obsolete subroutine, left for backward compatibility.
+        *************************************************************************/
+        public static double spline3interpolate(int n,
+            ref double[,] c,
+            double x)
+        {
+            double result = 0;
+            int i = 0;
+            int l = 0;
+            int half = 0;
+            int first = 0;
+            int middle = 0;
+
+            n = n-1;
+            l = n;
+            first = 0;
+            while( l>0 )
+            {
+                half = l/2;
+                middle = first+half;
+                if( c[4,middle]<x )
+                {
+                    first = middle+1;
+                    l = l-half-1;
+                }
+                else
+                {
+                    l = half;
+                }
+            }
+            i = first-1;
+            if( i<0 )
+            {
+                i = 0;
+            }
+            result = c[0,i]+(x-c[4,i])*(c[1,i]+(x-c[4,i])*(c[2,i]+c[3,i]*(x-c[4,i])));
+            return result;
+        }
+
+
+        /*************************************************************************
+        Internal subroutine. Heap sort.
+        *************************************************************************/
+        private static void heapsortpoints(ref double[] x,
+            ref double[] y,
+            int n)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int t = 0;
+            double tmp = 0;
+            bool isascending = new bool();
+            bool isdescending = new bool();
+
+            
+            //
+            // Test for already sorted set
+            //
+            isascending = true;
+            isdescending = true;
+            for(i=1; i<=n-1; i++)
+            {
+                isascending = isascending & x[i]>x[i-1];
+                isdescending = isdescending & x[i]<x[i-1];
+            }
+            if( isascending )
+            {
+                return;
+            }
+            if( isdescending )
+            {
+                for(i=0; i<=n-1; i++)
+                {
+                    j = n-1-i;
+                    if( j<=i )
+                    {
+                        break;
+                    }
+                    tmp = x[i];
+                    x[i] = x[j];
+                    x[j] = tmp;
+                    tmp = y[i];
+                    y[i] = y[j];
+                    y[j] = tmp;
+                }
+                return;
+            }
+            
+            //
+            // Special case: N=1
+            //
+            if( n==1 )
+            {
+                return;
+            }
+            
+            //
+            // General case
+            //
+            i = 2;
+            do
+            {
+                t = i;
+                while( t!=1 )
+                {
+                    k = t/2;
+                    if( x[k-1]>=x[t-1] )
+                    {
+                        t = 1;
+                    }
+                    else
+                    {
+                        tmp = x[k-1];
+                        x[k-1] = x[t-1];
+                        x[t-1] = tmp;
+                        tmp = y[k-1];
+                        y[k-1] = y[t-1];
+                        y[t-1] = tmp;
+                        t = k;
+                    }
+                }
+                i = i+1;
+            }
+            while( i<=n );
+            i = n-1;
+            do
+            {
+                tmp = x[i];
+                x[i] = x[0];
+                x[0] = tmp;
+                tmp = y[i];
+                y[i] = y[0];
+                y[0] = tmp;
+                t = 1;
+                while( t!=0 )
+                {
+                    k = 2*t;
+                    if( k>i )
+                    {
+                        t = 0;
+                    }
+                    else
+                    {
+                        if( k<i )
+                        {
+                            if( x[k]>x[k-1] )
+                            {
+                                k = k+1;
+                            }
+                        }
+                        if( x[t-1]>=x[k-1] )
+                        {
+                            t = 0;
+                        }
+                        else
+                        {
+                            tmp = x[k-1];
+                            x[k-1] = x[t-1];
+                            x[t-1] = tmp;
+                            tmp = y[k-1];
+                            y[k-1] = y[t-1];
+                            y[t-1] = tmp;
+                            t = k;
+                        }
+                    }
+                }
+                i = i-1;
+            }
+            while( i>=1 );
+        }
+
+
+        /*************************************************************************
+        Internal subroutine. Heap sort.
+        *************************************************************************/
+        private static void heapsortdpoints(ref double[] x,
+            ref double[] y,
+            ref double[] d,
+            int n)
+        {
+            int i = 0;
+            int j = 0;
+            int k = 0;
+            int t = 0;
+            double tmp = 0;
+            bool isascending = new bool();
+            bool isdescending = new bool();
+
+            
+            //
+            // Test for already sorted set
+            //
+            isascending = true;
+            isdescending = true;
+            for(i=1; i<=n-1; i++)
+            {
+                isascending = isascending & x[i]>x[i-1];
+                isdescending = isdescending & x[i]<x[i-1];
+            }
+            if( isascending )
+            {
+                return;
+            }
+            if( isdescending )
+            {
+                for(i=0; i<=n-1; i++)
+                {
+                    j = n-1-i;
+                    if( j<=i )
+                    {
+                        break;
+                    }
+                    tmp = x[i];
+                    x[i] = x[j];
+                    x[j] = tmp;
+                    tmp = y[i];
+                    y[i] = y[j];
+                    y[j] = tmp;
+                    tmp = d[i];
+                    d[i] = d[j];
+                    d[j] = tmp;
+                }
+                return;
+            }
+            
+            //
+            // Special case: N=1
+            //
+            if( n==1 )
+            {
+                return;
+            }
+            
+            //
+            // General case
+            //
+            i = 2;
+            do
+            {
+                t = i;
+                while( t!=1 )
+                {
+                    k = t/2;
+                    if( x[k-1]>=x[t-1] )
+                    {
+                        t = 1;
+                    }
+                    else
+                    {
+                        tmp = x[k-1];
+                        x[k-1] = x[t-1];
+                        x[t-1] = tmp;
+                        tmp = y[k-1];
+                        y[k-1] = y[t-1];
+                        y[t-1] = tmp;
+                        tmp = d[k-1];
+                        d[k-1] = d[t-1];
+                        d[t-1] = tmp;
+                        t = k;
+                    }
+                }
+                i = i+1;
+            }
+            while( i<=n );
+            i = n-1;
+            do
+            {
+                tmp = x[i];
+                x[i] = x[0];
+                x[0] = tmp;
+                tmp = y[i];
+                y[i] = y[0];
+                y[0] = tmp;
+                tmp = d[i];
+                d[i] = d[0];
+                d[0] = tmp;
+                t = 1;
+                while( t!=0 )
+                {
+                    k = 2*t;
+                    if( k>i )
+                    {
+                        t = 0;
+                    }
+                    else
+                    {
+                        if( k<i )
+                        {
+                            if( x[k]>x[k-1] )
+                            {
+                                k = k+1;
+                            }
+                        }
+                        if( x[t-1]>=x[k-1] )
+                        {
+                            t = 0;
+                        }
+                        else
+                        {
+                            tmp = x[k-1];
+                            x[k-1] = x[t-1];
+                            x[t-1] = tmp;
+                            tmp = y[k-1];
+                            y[k-1] = y[t-1];
+                            y[t-1] = tmp;
+                            tmp = d[k-1];
+                            d[k-1] = d[t-1];
+                            d[t-1] = tmp;
+                            t = k;
+                        }
+                    }
+                }
+                i = i-1;
+            }
+            while( i>=1 );
+        }
+
+
+        /*************************************************************************
+        Internal subroutine. Tridiagonal solver.
+        *************************************************************************/
+        private static void solvetridiagonal(double[] a,
+            double[] b,
+            double[] c,
+            double[] d,
+            int n,
+            ref double[] x)
+        {
+            int k = 0;
+            double t = 0;
+
+            a = (double[])a.Clone();
+            b = (double[])b.Clone();
+            c = (double[])c.Clone();
+            d = (double[])d.Clone();
+
+            x = new double[n-1+1];
+            a[0] = 0;
+            c[n-1] = 0;
+            for(k=1; k<=n-1; k++)
+            {
+                t = a[k]/b[k-1];
+                b[k] = b[k]-t*c[k-1];
+                d[k] = d[k]-t*d[k-1];
+            }
+            x[n-1] = d[n-1]/b[n-1];
+            for(k=n-2; k>=0; k--)
+            {
+                x[k] = (d[k]-c[k]*x[k+1])/b[k];
+            }
+        }
+
+
+        /*************************************************************************
+        Internal subroutine. Three-point differentiation
+        *************************************************************************/
+        private static double diffthreepoint(double t,
+            double x0,
+            double f0,
+            double x1,
+            double f1,
+            double x2,
+            double f2)
+        {
+            double result = 0;
+            double a = 0;
+            double b = 0;
+
+            t = t-x0;
+            x1 = x1-x0;
+            x2 = x2-x0;
+            a = (f2-f0-x2/x1*(f1-f0))/(AP.Math.Sqr(x2)-x1*x2);
+            b = (f1-f0-a*AP.Math.Sqr(x1))/x1;
+            result = 2*a*t+b;
+            return result;
+        }
+    }
+}
Index: trunk/sources/ALGLIB/svd.cs
===================================================================
--- trunk/sources/ALGLIB/svd.cs	(revision 2430)
+++ trunk/sources/ALGLIB/svd.cs	(revision 2430)
@@ -0,0 +1,610 @@
+/*************************************************************************
+Copyright (c) 2005-2007, Sergey Bochkanov (ALGLIB project).
+
+>>> SOURCE LICENSE >>>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation (www.fsf.org); either version 2 of the 
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+A copy of the GNU General Public License is available at
+http://www.fsf.org/licensing/licenses
+
+>>> END OF LICENSE >>>
+*************************************************************************/
+
+using System;
+
+namespace alglib
+{
+    public class svd
+    {
+        /*************************************************************************
+        Singular value decomposition of a rectangular matrix.
+
+        The algorithm calculates the singular value decomposition of a matrix of
+        size MxN: A = U * S * V^T
+
+        The algorithm finds the singular values and, optionally, matrices U and V^T.
+        The algorithm can find both first min(M,N) columns of matrix U and rows of
+        matrix V^T (singular vectors), and matrices U and V^T wholly (of sizes MxM
+        and NxN respectively).
+
+        Take into account that the subroutine does not return matrix V but V^T.
+
+        Input parameters:
+            A           -   matrix to be decomposed.
+                            Array whose indexes range within [0..M-1, 0..N-1].
+            M           -   number of rows in matrix A.
+            N           -   number of columns in matrix A.
+            UNeeded     -   0, 1 or 2. See the description of the parameter U.
+            VTNeeded    -   0, 1 or 2. See the description of the parameter VT.
+            AdditionalMemory -
+                            If the parameter:
+                             * equals 0, the algorithm doesnt use additional
+                               memory (lower requirements, lower performance).
+                             * equals 1, the algorithm uses additional
+                               memory of size min(M,N)*min(M,N) of real numbers.
+                               It often speeds up the algorithm.
+                             * equals 2, the algorithm uses additional
+                               memory of size M*min(M,N) of real numbers.
+                               It allows to get a maximum performance.
+                            The recommended value of the parameter is 2.
+
+        Output parameters:
+            W           -   contains singular values in descending order.
+            U           -   if UNeeded=0, U isn't changed, the left singular vectors
+                            are not calculated.
+                            if Uneeded=1, U contains left singular vectors (first
+                            min(M,N) columns of matrix U). Array whose indexes range
+                            within [0..M-1, 0..Min(M,N)-1].
+                            if UNeeded=2, U contains matrix U wholly. Array whose
+                            indexes range within [0..M-1, 0..M-1].
+            VT          -   if VTNeeded=0, VT isnt changed, the right singular vectors
+                            are not calculated.
+                            if VTNeeded=1, VT contains right singular vectors (first
+                            min(M,N) rows of matrix V^T). Array whose indexes range
+                            within [0..min(M,N)-1, 0..N-1].
+                            if VTNeeded=2, VT contains matrix V^T wholly. Array whose
+                            indexes range within [0..N-1, 0..N-1].
+
+          -- ALGLIB --
+             Copyright 2005 by Bochkanov Sergey
+        *************************************************************************/
+        public static bool rmatrixsvd(double[,] a,
+            int m,
+            int n,
+            int uneeded,
+            int vtneeded,
+            int additionalmemory,
+            ref double[] w,
+            ref double[,] u,
+            ref double[,] vt)
+        {
+            bool result = new bool();
+            double[] tauq = new double[0];
+            double[] taup = new double[0];
+            double[] tau = new double[0];
+            double[] e = new double[0];
+            double[] work = new double[0];
+            double[,] t2 = new double[0,0];
+            bool isupper = new bool();
+            int minmn = 0;
+            int ncu = 0;
+            int nrvt = 0;
+            int nru = 0;
+            int ncvt = 0;
+            int i = 0;
+            int j = 0;
+            int im1 = 0;
+            double sm = 0;
+
+            a = (double[,])a.Clone();
+
+            result = true;
+            if( m==0 | n==0 )
+            {
+                return result;
+            }
+            System.Diagnostics.Debug.Assert(uneeded>=0 & uneeded<=2, "SVDDecomposition: wrong parameters!");
+            System.Diagnostics.Debug.Assert(vtneeded>=0 & vtneeded<=2, "SVDDecomposition: wrong parameters!");
+            System.Diagnostics.Debug.Assert(additionalmemory>=0 & additionalmemory<=2, "SVDDecomposition: wrong parameters!");
+            
+            //
+            // initialize
+            //
+            minmn = Math.Min(m, n);
+            w = new double[minmn+1];
+            ncu = 0;
+            nru = 0;
+            if( uneeded==1 )
+            {
+                nru = m;
+                ncu = minmn;
+                u = new double[nru-1+1, ncu-1+1];
+            }
+            if( uneeded==2 )
+            {
+                nru = m;
+                ncu = m;
+                u = new double[nru-1+1, ncu-1+1];
+            }
+            nrvt = 0;
+            ncvt = 0;
+            if( vtneeded==1 )
+            {
+                nrvt = minmn;
+                ncvt = n;
+                vt = new double[nrvt-1+1, ncvt-1+1];
+            }
+            if( vtneeded==2 )
+            {
+                nrvt = n;
+                ncvt = n;
+                vt = new double[nrvt-1+1, ncvt-1+1];
+            }
+            
+            //
+            // M much larger than N
+            // Use bidiagonal reduction with QR-decomposition
+            //
+            if( m>1.6*n )
+            {
+                if( uneeded==0 )
+                {
+                    
+                    //
+                    // No left singular vectors to be computed
+                    //
+                    qr.rmatrixqr(ref a, m, n, ref tau);
+                    for(i=0; i<=n-1; i++)
+                    {
+                        for(j=0; j<=i-1; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.rmatrixbd(ref a, n, n, ref tauq, ref taup);
+                    bidiagonal.rmatrixbdunpackpt(ref a, n, n, ref taup, nrvt, ref vt);
+                    bidiagonal.rmatrixbdunpackdiagonals(ref a, n, n, ref isupper, ref w, ref e);
+                    result = bdsvd.rmatrixbdsvd(ref w, e, n, isupper, false, ref u, 0, ref a, 0, ref vt, ncvt);
+                    return result;
+                }
+                else
+                {
+                    
+                    //
+                    // Left singular vectors (may be full matrix U) to be computed
+                    //
+                    qr.rmatrixqr(ref a, m, n, ref tau);
+                    qr.rmatrixqrunpackq(ref a, m, n, ref tau, ncu, ref u);
+                    for(i=0; i<=n-1; i++)
+                    {
+                        for(j=0; j<=i-1; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.rmatrixbd(ref a, n, n, ref tauq, ref taup);
+                    bidiagonal.rmatrixbdunpackpt(ref a, n, n, ref taup, nrvt, ref vt);
+                    bidiagonal.rmatrixbdunpackdiagonals(ref a, n, n, ref isupper, ref w, ref e);
+                    if( additionalmemory<1 )
+                    {
+                        
+                        //
+                        // No additional memory can be used
+                        //
+                        bidiagonal.rmatrixbdmultiplybyq(ref a, n, n, ref tauq, ref u, m, n, true, false);
+                        result = bdsvd.rmatrixbdsvd(ref w, e, n, isupper, false, ref u, m, ref a, 0, ref vt, ncvt);
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Large U. Transforming intermediate matrix T2
+                        //
+                        work = new double[Math.Max(m, n)+1];
+                        bidiagonal.rmatrixbdunpackq(ref a, n, n, ref tauq, n, ref t2);
+                        blas.copymatrix(ref u, 0, m-1, 0, n-1, ref a, 0, m-1, 0, n-1);
+                        blas.inplacetranspose(ref t2, 0, n-1, 0, n-1, ref work);
+                        result = bdsvd.rmatrixbdsvd(ref w, e, n, isupper, false, ref u, 0, ref t2, n, ref vt, ncvt);
+                        blas.matrixmatrixmultiply(ref a, 0, m-1, 0, n-1, false, ref t2, 0, n-1, 0, n-1, true, 1.0, ref u, 0, m-1, 0, n-1, 0.0, ref work);
+                    }
+                    return result;
+                }
+            }
+            
+            //
+            // N much larger than M
+            // Use bidiagonal reduction with LQ-decomposition
+            //
+            if( n>1.6*m )
+            {
+                if( vtneeded==0 )
+                {
+                    
+                    //
+                    // No right singular vectors to be computed
+                    //
+                    lq.rmatrixlq(ref a, m, n, ref tau);
+                    for(i=0; i<=m-1; i++)
+                    {
+                        for(j=i+1; j<=m-1; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.rmatrixbd(ref a, m, m, ref tauq, ref taup);
+                    bidiagonal.rmatrixbdunpackq(ref a, m, m, ref tauq, ncu, ref u);
+                    bidiagonal.rmatrixbdunpackdiagonals(ref a, m, m, ref isupper, ref w, ref e);
+                    work = new double[m+1];
+                    blas.inplacetranspose(ref u, 0, nru-1, 0, ncu-1, ref work);
+                    result = bdsvd.rmatrixbdsvd(ref w, e, m, isupper, false, ref a, 0, ref u, nru, ref vt, 0);
+                    blas.inplacetranspose(ref u, 0, nru-1, 0, ncu-1, ref work);
+                    return result;
+                }
+                else
+                {
+                    
+                    //
+                    // Right singular vectors (may be full matrix VT) to be computed
+                    //
+                    lq.rmatrixlq(ref a, m, n, ref tau);
+                    lq.rmatrixlqunpackq(ref a, m, n, ref tau, nrvt, ref vt);
+                    for(i=0; i<=m-1; i++)
+                    {
+                        for(j=i+1; j<=m-1; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.rmatrixbd(ref a, m, m, ref tauq, ref taup);
+                    bidiagonal.rmatrixbdunpackq(ref a, m, m, ref tauq, ncu, ref u);
+                    bidiagonal.rmatrixbdunpackdiagonals(ref a, m, m, ref isupper, ref w, ref e);
+                    work = new double[Math.Max(m, n)+1];
+                    blas.inplacetranspose(ref u, 0, nru-1, 0, ncu-1, ref work);
+                    if( additionalmemory<1 )
+                    {
+                        
+                        //
+                        // No additional memory available
+                        //
+                        bidiagonal.rmatrixbdmultiplybyp(ref a, m, m, ref taup, ref vt, m, n, false, true);
+                        result = bdsvd.rmatrixbdsvd(ref w, e, m, isupper, false, ref a, 0, ref u, nru, ref vt, n);
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Large VT. Transforming intermediate matrix T2
+                        //
+                        bidiagonal.rmatrixbdunpackpt(ref a, m, m, ref taup, m, ref t2);
+                        result = bdsvd.rmatrixbdsvd(ref w, e, m, isupper, false, ref a, 0, ref u, nru, ref t2, m);
+                        blas.copymatrix(ref vt, 0, m-1, 0, n-1, ref a, 0, m-1, 0, n-1);
+                        blas.matrixmatrixmultiply(ref t2, 0, m-1, 0, m-1, false, ref a, 0, m-1, 0, n-1, false, 1.0, ref vt, 0, m-1, 0, n-1, 0.0, ref work);
+                    }
+                    blas.inplacetranspose(ref u, 0, nru-1, 0, ncu-1, ref work);
+                    return result;
+                }
+            }
+            
+            //
+            // M<=N
+            // We can use inplace transposition of U to get rid of columnwise operations
+            //
+            if( m<=n )
+            {
+                bidiagonal.rmatrixbd(ref a, m, n, ref tauq, ref taup);
+                bidiagonal.rmatrixbdunpackq(ref a, m, n, ref tauq, ncu, ref u);
+                bidiagonal.rmatrixbdunpackpt(ref a, m, n, ref taup, nrvt, ref vt);
+                bidiagonal.rmatrixbdunpackdiagonals(ref a, m, n, ref isupper, ref w, ref e);
+                work = new double[m+1];
+                blas.inplacetranspose(ref u, 0, nru-1, 0, ncu-1, ref work);
+                result = bdsvd.rmatrixbdsvd(ref w, e, minmn, isupper, false, ref a, 0, ref u, nru, ref vt, ncvt);
+                blas.inplacetranspose(ref u, 0, nru-1, 0, ncu-1, ref work);
+                return result;
+            }
+            
+            //
+            // Simple bidiagonal reduction
+            //
+            bidiagonal.rmatrixbd(ref a, m, n, ref tauq, ref taup);
+            bidiagonal.rmatrixbdunpackq(ref a, m, n, ref tauq, ncu, ref u);
+            bidiagonal.rmatrixbdunpackpt(ref a, m, n, ref taup, nrvt, ref vt);
+            bidiagonal.rmatrixbdunpackdiagonals(ref a, m, n, ref isupper, ref w, ref e);
+            if( additionalmemory<2 | uneeded==0 )
+            {
+                
+                //
+                // We cant use additional memory or there is no need in such operations
+                //
+                result = bdsvd.rmatrixbdsvd(ref w, e, minmn, isupper, false, ref u, nru, ref a, 0, ref vt, ncvt);
+            }
+            else
+            {
+                
+                //
+                // We can use additional memory
+                //
+                t2 = new double[minmn-1+1, m-1+1];
+                blas.copyandtranspose(ref u, 0, m-1, 0, minmn-1, ref t2, 0, minmn-1, 0, m-1);
+                result = bdsvd.rmatrixbdsvd(ref w, e, minmn, isupper, false, ref u, 0, ref t2, m, ref vt, ncvt);
+                blas.copyandtranspose(ref t2, 0, minmn-1, 0, m-1, ref u, 0, m-1, 0, minmn-1);
+            }
+            return result;
+        }
+
+
+        /*************************************************************************
+        Obsolete 1-based subroutine.
+        See RMatrixSVD for 0-based replacement.
+        *************************************************************************/
+        public static bool svddecomposition(double[,] a,
+            int m,
+            int n,
+            int uneeded,
+            int vtneeded,
+            int additionalmemory,
+            ref double[] w,
+            ref double[,] u,
+            ref double[,] vt)
+        {
+            bool result = new bool();
+            double[] tauq = new double[0];
+            double[] taup = new double[0];
+            double[] tau = new double[0];
+            double[] e = new double[0];
+            double[] work = new double[0];
+            double[,] t2 = new double[0,0];
+            bool isupper = new bool();
+            int minmn = 0;
+            int ncu = 0;
+            int nrvt = 0;
+            int nru = 0;
+            int ncvt = 0;
+            int i = 0;
+            int j = 0;
+            int im1 = 0;
+            double sm = 0;
+
+            a = (double[,])a.Clone();
+
+            result = true;
+            if( m==0 | n==0 )
+            {
+                return result;
+            }
+            System.Diagnostics.Debug.Assert(uneeded>=0 & uneeded<=2, "SVDDecomposition: wrong parameters!");
+            System.Diagnostics.Debug.Assert(vtneeded>=0 & vtneeded<=2, "SVDDecomposition: wrong parameters!");
+            System.Diagnostics.Debug.Assert(additionalmemory>=0 & additionalmemory<=2, "SVDDecomposition: wrong parameters!");
+            
+            //
+            // initialize
+            //
+            minmn = Math.Min(m, n);
+            w = new double[minmn+1];
+            ncu = 0;
+            nru = 0;
+            if( uneeded==1 )
+            {
+                nru = m;
+                ncu = minmn;
+                u = new double[nru+1, ncu+1];
+            }
+            if( uneeded==2 )
+            {
+                nru = m;
+                ncu = m;
+                u = new double[nru+1, ncu+1];
+            }
+            nrvt = 0;
+            ncvt = 0;
+            if( vtneeded==1 )
+            {
+                nrvt = minmn;
+                ncvt = n;
+                vt = new double[nrvt+1, ncvt+1];
+            }
+            if( vtneeded==2 )
+            {
+                nrvt = n;
+                ncvt = n;
+                vt = new double[nrvt+1, ncvt+1];
+            }
+            
+            //
+            // M much larger than N
+            // Use bidiagonal reduction with QR-decomposition
+            //
+            if( m>1.6*n )
+            {
+                if( uneeded==0 )
+                {
+                    
+                    //
+                    // No left singular vectors to be computed
+                    //
+                    qr.qrdecomposition(ref a, m, n, ref tau);
+                    for(i=2; i<=n; i++)
+                    {
+                        for(j=1; j<=i-1; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.tobidiagonal(ref a, n, n, ref tauq, ref taup);
+                    bidiagonal.unpackptfrombidiagonal(ref a, n, n, ref taup, nrvt, ref vt);
+                    bidiagonal.unpackdiagonalsfrombidiagonal(ref a, n, n, ref isupper, ref w, ref e);
+                    result = bdsvd.bidiagonalsvddecomposition(ref w, e, n, isupper, false, ref u, 0, ref a, 0, ref vt, ncvt);
+                    return result;
+                }
+                else
+                {
+                    
+                    //
+                    // Left singular vectors (may be full matrix U) to be computed
+                    //
+                    qr.qrdecomposition(ref a, m, n, ref tau);
+                    qr.unpackqfromqr(ref a, m, n, ref tau, ncu, ref u);
+                    for(i=2; i<=n; i++)
+                    {
+                        for(j=1; j<=i-1; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.tobidiagonal(ref a, n, n, ref tauq, ref taup);
+                    bidiagonal.unpackptfrombidiagonal(ref a, n, n, ref taup, nrvt, ref vt);
+                    bidiagonal.unpackdiagonalsfrombidiagonal(ref a, n, n, ref isupper, ref w, ref e);
+                    if( additionalmemory<1 )
+                    {
+                        
+                        //
+                        // No additional memory can be used
+                        //
+                        bidiagonal.multiplybyqfrombidiagonal(ref a, n, n, ref tauq, ref u, m, n, true, false);
+                        result = bdsvd.bidiagonalsvddecomposition(ref w, e, n, isupper, false, ref u, m, ref a, 0, ref vt, ncvt);
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Large U. Transforming intermediate matrix T2
+                        //
+                        work = new double[Math.Max(m, n)+1];
+                        bidiagonal.unpackqfrombidiagonal(ref a, n, n, ref tauq, n, ref t2);
+                        blas.copymatrix(ref u, 1, m, 1, n, ref a, 1, m, 1, n);
+                        blas.inplacetranspose(ref t2, 1, n, 1, n, ref work);
+                        result = bdsvd.bidiagonalsvddecomposition(ref w, e, n, isupper, false, ref u, 0, ref t2, n, ref vt, ncvt);
+                        blas.matrixmatrixmultiply(ref a, 1, m, 1, n, false, ref t2, 1, n, 1, n, true, 1.0, ref u, 1, m, 1, n, 0.0, ref work);
+                    }
+                    return result;
+                }
+            }
+            
+            //
+            // N much larger than M
+            // Use bidiagonal reduction with LQ-decomposition
+            //
+            if( n>1.6*m )
+            {
+                if( vtneeded==0 )
+                {
+                    
+                    //
+                    // No right singular vectors to be computed
+                    //
+                    lq.lqdecomposition(ref a, m, n, ref tau);
+                    for(i=1; i<=m-1; i++)
+                    {
+                        for(j=i+1; j<=m; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.tobidiagonal(ref a, m, m, ref tauq, ref taup);
+                    bidiagonal.unpackqfrombidiagonal(ref a, m, m, ref tauq, ncu, ref u);
+                    bidiagonal.unpackdiagonalsfrombidiagonal(ref a, m, m, ref isupper, ref w, ref e);
+                    work = new double[m+1];
+                    blas.inplacetranspose(ref u, 1, nru, 1, ncu, ref work);
+                    result = bdsvd.bidiagonalsvddecomposition(ref w, e, m, isupper, false, ref a, 0, ref u, nru, ref vt, 0);
+                    blas.inplacetranspose(ref u, 1, nru, 1, ncu, ref work);
+                    return result;
+                }
+                else
+                {
+                    
+                    //
+                    // Right singular vectors (may be full matrix VT) to be computed
+                    //
+                    lq.lqdecomposition(ref a, m, n, ref tau);
+                    lq.unpackqfromlq(ref a, m, n, ref tau, nrvt, ref vt);
+                    for(i=1; i<=m-1; i++)
+                    {
+                        for(j=i+1; j<=m; j++)
+                        {
+                            a[i,j] = 0;
+                        }
+                    }
+                    bidiagonal.tobidiagonal(ref a, m, m, ref tauq, ref taup);
+                    bidiagonal.unpackqfrombidiagonal(ref a, m, m, ref tauq, ncu, ref u);
+                    bidiagonal.unpackdiagonalsfrombidiagonal(ref a, m, m, ref isupper, ref w, ref e);
+                    work = new double[Math.Max(m, n)+1];
+                    blas.inplacetranspose(ref u, 1, nru, 1, ncu, ref work);
+                    if( additionalmemory<1 )
+                    {
+                        
+                        //
+                        // No additional memory available
+                        //
+                        bidiagonal.multiplybypfrombidiagonal(ref a, m, m, ref taup, ref vt, m, n, false, true);
+                        result = bdsvd.bidiagonalsvddecomposition(ref w, e, m, isupper, false, ref a, 0, ref u, nru, ref vt, n);
+                    }
+                    else
+                    {
+                        
+                        //
+                        // Large VT. Transforming intermediate matrix T2
+                        //
+                        bidiagonal.unpackptfrombidiagonal(ref a, m, m, ref taup, m, ref t2);
+                        result = bdsvd.bidiagonalsvddecomposition(ref w, e, m, isupper, false, ref a, 0, ref u, nru, ref t2, m);
+                        blas.copymatrix(ref vt, 1, m, 1, n, ref a, 1, m, 1, n);
+                        blas.matrixmatrixmultiply(ref t2, 1, m, 1, m, false, ref a, 1, m, 1, n, false, 1.0, ref vt, 1, m, 1, n, 0.0, ref work);
+                    }
+                    blas.inplacetranspose(ref u, 1, nru, 1, ncu, ref work);
+                    return result;
+                }
+            }
+            
+            //
+            // M<=N
+            // We can use inplace transposition of U to get rid of columnwise operations
+            //
+            if( m<=n )
+            {
+                bidiagonal.tobidiagonal(ref a, m, n, ref tauq, ref taup);
+                bidiagonal.unpackqfrombidiagonal(ref a, m, n, ref tauq, ncu, ref u);
+                bidiagonal.unpackptfrombidiagonal(ref a, m, n, ref taup, nrvt, ref vt);
+                bidiagonal.unpackdiagonalsfrombidiagonal(ref a, m, n, ref isupper, ref w, ref e);
+                work = new double[m+1];
+                blas.inplacetranspose(ref u, 1, nru, 1, ncu, ref work);
+                result = bdsvd.bidiagonalsvddecomposition(ref w, e, minmn, isupper, false, ref a, 0, ref u, nru, ref vt, ncvt);
+                blas.inplacetranspose(ref u, 1, nru, 1, ncu, ref work);
+                return result;
+            }
+            
+            //
+            // Simple bidiagonal reduction
+            //
+            bidiagonal.tobidiagonal(ref a, m, n, ref tauq, ref taup);
+            bidiagonal.unpackqfrombidiagonal(ref a, m, n, ref tauq, ncu, ref u);
+            bidiagonal.unpackptfrombidiagonal(ref a, m, n, ref taup, nrvt, ref vt);
+            bidiagonal.unpackdiagonalsfrombidiagonal(ref a, m, n, ref isupper, ref w, ref e);
+            if( additionalmemory<2 | uneeded==0 )
+            {
+                
+                //
+                // We cant use additional memory or there is no need in such operations
+                //
+                result = bdsvd.bidiagonalsvddecomposition(ref w, e, minmn, isupper, false, ref u, nru, ref a, 0, ref vt, ncvt);
+            }
+            else
+            {
+                
+                //
+                // We can use additional memory
+                //
+                t2 = new double[minmn+1, m+1];
+                blas.copyandtranspose(ref u, 1, m, 1, minmn, ref t2, 1, minmn, 1, m);
+                result = bdsvd.bidiagonalsvddecomposition(ref w, e, minmn, isupper, false, ref u, 0, ref t2, m, ref vt, ncvt);
+                blas.copyandtranspose(ref t2, 1, minmn, 1, m, ref u, 1, m, 1, minmn);
+            }
+            return result;
+        }
+    }
+}
Index: trunk/sources/HeuristicLab.LinearRegression/3.2/HeuristicLab.LinearRegression-3.2.csproj
===================================================================
--- trunk/sources/HeuristicLab.LinearRegression/3.2/HeuristicLab.LinearRegression-3.2.csproj	(revision 2429)
+++ trunk/sources/HeuristicLab.LinearRegression/3.2/HeuristicLab.LinearRegression-3.2.csproj	(revision 2430)
@@ -80,15 +80,4 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="alglib\ap.cs" />
-    <Compile Include="alglib\bdsvd.cs" />
-    <Compile Include="alglib\bidiagonal.cs" />
-    <Compile Include="alglib\blas.cs" />
-    <Compile Include="alglib\leastsquares.cs" />
-    <Compile Include="alglib\lq.cs" />
-    <Compile Include="alglib\qr.cs" />
-    <Compile Include="alglib\reflections.cs" />
-    <Compile Include="alglib\rotations.cs" />
-    <Compile Include="alglib\spline3.cs" />
-    <Compile Include="alglib\svd.cs" />
     <Compile Include="LinearClassification.cs" />
     <Compile Include="HeuristicLabLinearRegressionPlugin.cs" />
@@ -103,4 +92,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\ALGLIB\ALGLIB.csproj">
+      <Project>{44456535-5DB5-454A-BE2B-7426DA6700B6}</Project>
+      <Name>ALGLIB</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Common\3.2\HeuristicLab.Common-3.2.csproj">
       <Project>{1FC004FC-59AF-4249-B1B6-FF25873A20E4}</Project>
@@ -160,9 +153,4 @@
     </ProjectReference>
   </ItemGroup>
-  <ItemGroup>
-    <Content Include="ALGLIB License.txt">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </Content>
-  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
Index: trunk/sources/HeuristicLab.LinearRegression/3.2/HeuristicLabLinearRegressionPlugin.cs
===================================================================
--- trunk/sources/HeuristicLab.LinearRegression/3.2/HeuristicLabLinearRegressionPlugin.cs	(revision 2429)
+++ trunk/sources/HeuristicLab.LinearRegression/3.2/HeuristicLabLinearRegressionPlugin.cs	(revision 2430)
@@ -27,5 +27,5 @@
   [ClassInfo(Name = "HeuristicLab.LinearRegression-3.2")]
   [PluginFile(Filename = "HeuristicLab.LinearRegression-3.2.dll", Filetype = PluginFileType.Assembly)]
-  [PluginFile(Filename = "ALGLIB License.txt", Filetype = PluginFileType.License)]
+  [Dependency(Dependency = "ALGLIB")]
   [Dependency(Dependency = "HeuristicLab.Common-3.2")]
   [Dependency(Dependency = "HeuristicLab.Core-3.2")]
Index: trunk/sources/HeuristicLab.LinearRegression/3.2/LinearRegressionOperator.cs
===================================================================
--- trunk/sources/HeuristicLab.LinearRegression/3.2/LinearRegressionOperator.cs	(revision 2429)
+++ trunk/sources/HeuristicLab.LinearRegression/3.2/LinearRegressionOperator.cs	(revision 2430)
@@ -104,5 +104,5 @@
       for (int i = 0; i < weights.Length; i++) weights[i] = 1.0;
       // call external ALGLIB solver
-      leastsquares.buildgeneralleastsquares(ref targetVector, ref weights, ref inputMatrix, inputMatrix.GetLength(0), inputMatrix.GetLength(1), ref coefficients);
+      alglib.leastsquares.buildgeneralleastsquares(ref targetVector, ref weights, ref inputMatrix, inputMatrix.GetLength(0), inputMatrix.GetLength(1), ref coefficients);
 
       return coefficients;
Index: trunk/sources/HeuristicLab.Modeling/3.2/HeuristicLab.Modeling-3.2.csproj
===================================================================
--- trunk/sources/HeuristicLab.Modeling/3.2/HeuristicLab.Modeling-3.2.csproj	(revision 2429)
+++ trunk/sources/HeuristicLab.Modeling/3.2/HeuristicLab.Modeling-3.2.csproj	(revision 2430)
@@ -82,6 +82,4 @@
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="alglib\ap.cs" />
-    <Compile Include="alglib\correlation.cs" />
     <Compile Include="AnalyzerModel.cs" />
     <Compile Include="BestSolutionStorer.cs" />
@@ -132,4 +130,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\ALGLIB\ALGLIB.csproj">
+      <Project>{44456535-5DB5-454A-BE2B-7426DA6700B6}</Project>
+      <Name>ALGLIB</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Common\3.2\HeuristicLab.Common-3.2.csproj">
       <Project>{1FC004FC-59AF-4249-B1B6-FF25873A20E4}</Project>
Index: trunk/sources/HeuristicLab.Modeling/3.2/HeuristicLabModelingPlugin.cs
===================================================================
--- trunk/sources/HeuristicLab.Modeling/3.2/HeuristicLabModelingPlugin.cs	(revision 2429)
+++ trunk/sources/HeuristicLab.Modeling/3.2/HeuristicLabModelingPlugin.cs	(revision 2430)
@@ -28,4 +28,5 @@
   [ClassInfo(Name = "HeuristicLab.Modeling-3.2")]
   [PluginFile(Filename = "HeuristicLab.Modeling-3.2.dll", Filetype = PluginFileType.Assembly)]
+  [Dependency(Dependency = "ALGLIB")]
   [Dependency(Dependency = "HeuristicLab.Common-3.2")]
   [Dependency(Dependency = "HeuristicLab.Core-3.2")]
Index: trunk/sources/HeuristicLab.sln
===================================================================
--- trunk/sources/HeuristicLab.sln	(revision 2429)
+++ trunk/sources/HeuristicLab.sln	(revision 2430)
@@ -253,4 +253,6 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeuristicLab.BackgroundProcessing-3.2", "HeuristicLab.BackgroundProcessing\3.2\HeuristicLab.BackgroundProcessing-3.2.csproj", "{589BECAE-69B0-4EB1-8C06-C073531762B7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ALGLIB", "ALGLIB\ALGLIB.csproj", "{44456535-5DB5-454A-BE2B-7426DA6700B6}"
 EndProject
 Global
@@ -4064,4 +4066,32 @@
 		{589BECAE-69B0-4EB1-8C06-C073531762B7}.Visualization Debug|x86.ActiveCfg = Debug|x86
 		{589BECAE-69B0-4EB1-8C06-C073531762B7}.Visualization Debug|x86.Build.0 = Debug|x86
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.CEDMA Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.CEDMA Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.CEDMA Debug|x64.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.CEDMA Debug|x86.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Debug|x64.ActiveCfg = Debug|x64
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Debug|x64.Build.0 = Debug|x64
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Debug|x86.ActiveCfg = Debug|x86
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Debug|x86.Build.0 = Debug|x86
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Modeling Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Modeling Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Modeling Debug|x64.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Modeling Debug|x86.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Release|x64.ActiveCfg = Release|x64
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Release|x64.Build.0 = Release|x64
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Release|x86.ActiveCfg = Release|x86
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Release|x86.Build.0 = Release|x86
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.v3.2 Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.v3.2 Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.v3.2 Debug|x64.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.v3.2 Debug|x86.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Visualization Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Visualization Debug|Any CPU.Build.0 = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Visualization Debug|x64.ActiveCfg = Debug|Any CPU
+		{44456535-5DB5-454A-BE2B-7426DA6700B6}.Visualization Debug|x86.ActiveCfg = Debug|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
@@ -4159,4 +4189,5 @@
 		{3127719F-110E-4558-8845-98559DBB422D} = {410732DB-725A-4824-896B-C298978343C0}
 		{589BECAE-69B0-4EB1-8C06-C073531762B7} = {410732DB-725A-4824-896B-C298978343C0}
+		{44456535-5DB5-454A-BE2B-7426DA6700B6} = {410732DB-725A-4824-896B-C298978343C0}
 		{A9E282EA-180F-4233-B809-AEDF0787545C} = {78982D7C-D63D-4A3D-AE1F-F58AC007603B}
 		{BF7D9494-A586-457B-8DF9-ED599F9E6A71} = {78982D7C-D63D-4A3D-AE1F-F58AC007603B}
Index: trunk/sources/HeuristicLab/CopyAssemblies.cmd
===================================================================
--- trunk/sources/HeuristicLab/CopyAssemblies.cmd	(revision 2429)
+++ trunk/sources/HeuristicLab/CopyAssemblies.cmd	(revision 2430)
@@ -7,4 +7,6 @@
 mkdir plugins\backup
 
+copy "%SolutionDir%\ALGLIB\%Outdir%\ALGLIB.dll" .\plugins
+copy "%SolutionDir%\ALGLIB\%Outdir%\ALGLIB License.txt" .\plugins
 copy "%SolutionDir%\HeuristicLab.AdvancedOptimizationFrontend\3.2\%Outdir%\HeuristicLab.AdvancedOptimizationFrontend-3.2.dll" .\plugins
 copy "%SolutionDir%\HeuristicLab.AdvancedOptimizationFrontend\3.3\%Outdir%\HeuristicLab.AdvancedOptimizationFrontend-3.3.dll" .\plugins
