You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mahout.apache.org by ra...@apache.org on 2018/09/08 23:35:19 UTC

[15/15] mahout git commit: NO-JIRA Trevors updates

NO-JIRA Trevors updates


Project: http://git-wip-us.apache.org/repos/asf/mahout/repo
Commit: http://git-wip-us.apache.org/repos/asf/mahout/commit/545648f6
Tree: http://git-wip-us.apache.org/repos/asf/mahout/tree/545648f6
Diff: http://git-wip-us.apache.org/repos/asf/mahout/diff/545648f6

Branch: refs/heads/branch-0.14.0
Commit: 545648f6a8f69139757f0328e924c6b16a839441
Parents: 49ad8cb
Author: Trevor a.k.a @rawkintrevo <tr...@gmail.com>
Authored: Sat Sep 8 18:34:43 2018 -0500
Committer: Trevor a.k.a @rawkintrevo <tr...@gmail.com>
Committed: Sat Sep 8 18:34:43 2018 -0500

----------------------------------------------------------------------
 .../apache/mahout/collections/Arithmetic.java   |  489 ++++
 .../apache/mahout/collections/Constants.java    |   75 +
 .../org/apache/mahout/common/RandomUtils.java   |  100 +
 .../org/apache/mahout/common/RandomWrapper.java |  105 +
 .../org/apache/mahout/math/AbstractMatrix.java  |  834 +++++++
 .../org/apache/mahout/math/AbstractVector.java  |  684 ++++++
 .../java/org/apache/mahout/math/Algebra.java    |   73 +
 .../java/org/apache/mahout/math/Arrays.java     |  662 +++++
 .../org/apache/mahout/math/BinarySearch.java    |  403 +++
 .../mahout/math/CardinalityException.java       |   30 +
 .../java/org/apache/mahout/math/Centroid.java   |   89 +
 .../mahout/math/CholeskyDecomposition.java      |  227 ++
 .../org/apache/mahout/math/ConstantVector.java  |  177 ++
 .../apache/mahout/math/DelegatingVector.java    |  336 +++
 .../org/apache/mahout/math/DenseMatrix.java     |  193 ++
 .../mahout/math/DenseSymmetricMatrix.java       |   62 +
 .../org/apache/mahout/math/DenseVector.java     |  442 ++++
 .../org/apache/mahout/math/DiagonalMatrix.java  |  378 +++
 .../org/apache/mahout/math/FileBasedMatrix.java |  185 ++
 .../math/FileBasedSparseBinaryMatrix.java       |  535 ++++
 .../mahout/math/FunctionalMatrixView.java       |   99 +
 .../org/apache/mahout/math/IndexException.java  |   30 +
 .../apache/mahout/math/LengthCachingVector.java |   35 +
 .../java/org/apache/mahout/math/Matrices.java   |  167 ++
 .../java/org/apache/mahout/math/Matrix.java     |  413 ++++
 .../org/apache/mahout/math/MatrixSlice.java     |   36 +
 .../org/apache/mahout/math/MatrixTimesOps.java  |   35 +
 .../apache/mahout/math/MatrixVectorView.java    |  292 +++
 .../java/org/apache/mahout/math/MatrixView.java |  160 ++
 .../java/org/apache/mahout/math/MurmurHash.java |  158 ++
 .../org/apache/mahout/math/MurmurHash3.java     |   84 +
 .../org/apache/mahout/math/NamedVector.java     |  328 +++
 .../apache/mahout/math/OldQRDecomposition.java  |  234 ++
 .../mahout/math/OrderedIntDoubleMapping.java    |  265 ++
 .../mahout/math/OrthonormalityVerifier.java     |   46 +
 .../apache/mahout/math/PermutedVectorView.java  |  250 ++
 .../apache/mahout/math/PersistentObject.java    |   58 +
 .../org/apache/mahout/math/PivotedMatrix.java   |  288 +++
 .../main/java/org/apache/mahout/math/QR.java    |   27 +
 .../org/apache/mahout/math/QRDecomposition.java |  181 ++
 .../mahout/math/RandomAccessSparseVector.java   |  303 +++
 .../apache/mahout/math/RandomTrinaryMatrix.java |  146 ++
 .../math/SequentialAccessSparseVector.java      |  379 +++
 .../mahout/math/SingularValueDecomposition.java |  669 +++++
 .../java/org/apache/mahout/math/Sorting.java    | 2297 ++++++++++++++++++
 .../apache/mahout/math/SparseColumnMatrix.java  |  220 ++
 .../org/apache/mahout/math/SparseMatrix.java    |  245 ++
 .../org/apache/mahout/math/SparseRowMatrix.java |  289 +++
 .../java/org/apache/mahout/math/Swapper.java    |   35 +
 .../mahout/math/TransposedMatrixView.java       |  147 ++
 .../org/apache/mahout/math/UpperTriangular.java |  160 ++
 .../java/org/apache/mahout/math/Vector.java     |  434 ++++
 .../mahout/math/VectorBinaryAggregate.java      |  481 ++++
 .../apache/mahout/math/VectorBinaryAssign.java  |  667 +++++
 .../org/apache/mahout/math/VectorIterable.java  |   56 +
 .../java/org/apache/mahout/math/VectorView.java |  238 ++
 .../org/apache/mahout/math/WeightedVector.java  |   87 +
 .../mahout/math/WeightedVectorComparator.java   |   54 +
 .../math/als/AlternatingLeastSquaresSolver.java |  116 +
 ...itFeedbackAlternatingLeastSquaresSolver.java |  171 ++
 .../math/decomposer/AsyncEigenVerifier.java     |   80 +
 .../mahout/math/decomposer/EigenStatus.java     |   50 +
 .../math/decomposer/SimpleEigenVerifier.java    |   41 +
 .../math/decomposer/SingularVectorVerifier.java |   25 +
 .../math/decomposer/hebbian/EigenUpdater.java   |   25 +
 .../math/decomposer/hebbian/HebbianSolver.java  |  342 +++
 .../math/decomposer/hebbian/HebbianUpdater.java |   71 +
 .../math/decomposer/hebbian/TrainingState.java  |  143 ++
 .../math/decomposer/lanczos/LanczosSolver.java  |  213 ++
 .../math/decomposer/lanczos/LanczosState.java   |  107 +
 .../org/apache/mahout/math/flavor/BackEnum.java |   26 +
 .../apache/mahout/math/flavor/MatrixFlavor.java |   82 +
 .../math/flavor/TraversingStructureEnum.java    |   48 +
 .../math/function/DoubleDoubleFunction.java     |   98 +
 .../mahout/math/function/DoubleFunction.java    |   48 +
 .../mahout/math/function/FloatFunction.java     |   36 +
 .../apache/mahout/math/function/Functions.java  | 1730 +++++++++++++
 .../mahout/math/function/IntFunction.java       |   41 +
 .../math/function/IntIntDoubleFunction.java     |   43 +
 .../mahout/math/function/IntIntFunction.java    |   25 +
 .../org/apache/mahout/math/function/Mult.java   |   71 +
 .../math/function/ObjectObjectProcedure.java    |   40 +
 .../mahout/math/function/ObjectProcedure.java   |   47 +
 .../apache/mahout/math/function/PlusMult.java   |  123 +
 .../math/function/SquareRootFunction.java       |   26 +
 .../mahout/math/function/TimesFunction.java     |   77 +
 .../mahout/math/function/VectorFunction.java    |   27 +
 .../mahout/math/function/package-info.java      |    4 +
 .../apache/mahout/math/jet/math/Arithmetic.java |  328 +++
 .../apache/mahout/math/jet/math/Constants.java  |   49 +
 .../apache/mahout/math/jet/math/Polynomial.java |   98 +
 .../mahout/math/jet/math/package-info.java      |    5 +
 .../random/AbstractContinousDistribution.java   |   51 +
 .../random/AbstractDiscreteDistribution.java    |   27 +
 .../math/jet/random/AbstractDistribution.java   |   87 +
 .../mahout/math/jet/random/Exponential.java     |   81 +
 .../apache/mahout/math/jet/random/Gamma.java    |  302 +++
 .../math/jet/random/NegativeBinomial.java       |  106 +
 .../apache/mahout/math/jet/random/Normal.java   |  110 +
 .../apache/mahout/math/jet/random/Poisson.java  |  296 +++
 .../apache/mahout/math/jet/random/Uniform.java  |  164 ++
 .../math/jet/random/engine/MersenneTwister.java |  275 +++
 .../math/jet/random/engine/RandomEngine.java    |  169 ++
 .../math/jet/random/engine/package-info.java    |    7 +
 .../math/jet/random/sampling/RandomSampler.java |  503 ++++
 .../org/apache/mahout/math/jet/stat/Gamma.java  |  681 ++++++
 .../mahout/math/jet/stat/Probability.java       |  203 ++
 .../mahout/math/jet/stat/package-info.java      |    5 +
 .../apache/mahout/math/list/AbstractList.java   |  247 ++
 .../mahout/math/list/AbstractObjectList.java    |   80 +
 .../mahout/math/list/ObjectArrayList.java       |  419 ++++
 .../mahout/math/list/SimpleLongArrayList.java   |  102 +
 .../apache/mahout/math/list/package-info.java   |  144 ++
 .../apache/mahout/math/map/HashFunctions.java   |  115 +
 .../org/apache/mahout/math/map/OpenHashMap.java |  652 +++++
 .../org/apache/mahout/math/map/PrimeFinder.java |  145 ++
 .../mahout/math/map/QuickOpenIntIntHashMap.java |  215 ++
 .../apache/mahout/math/map/package-info.java    |  250 ++
 .../org/apache/mahout/math/package-info.java    |    4 +
 .../math/random/AbstractSamplerFunction.java    |   39 +
 .../mahout/math/random/ChineseRestaurant.java   |  111 +
 .../apache/mahout/math/random/Empirical.java    |  124 +
 .../apache/mahout/math/random/IndianBuffet.java |  157 ++
 .../org/apache/mahout/math/random/Missing.java  |   59 +
 .../apache/mahout/math/random/MultiNormal.java  |  118 +
 .../apache/mahout/math/random/Multinomial.java  |  202 ++
 .../org/apache/mahout/math/random/Normal.java   |   40 +
 .../mahout/math/random/PoissonSampler.java      |   67 +
 .../org/apache/mahout/math/random/Sampler.java  |   25 +
 .../mahout/math/random/WeightedThing.java       |   71 +
 .../org/apache/mahout/math/set/AbstractSet.java |  188 ++
 .../org/apache/mahout/math/set/HashUtils.java   |   56 +
 .../org/apache/mahout/math/set/OpenHashSet.java |  548 +++++
 .../math/solver/ConjugateGradientSolver.java    |  213 ++
 .../mahout/math/solver/EigenDecomposition.java  |  892 +++++++
 .../mahout/math/solver/JacobiConditioner.java   |   47 +
 .../org/apache/mahout/math/solver/LSMR.java     |  565 +++++
 .../mahout/math/solver/Preconditioner.java      |   36 +
 .../mahout/math/ssvd/SequentialBigSvd.java      |   69 +
 .../apache/mahout/math/stats/LogLikelihood.java |  220 ++
 .../math/stats/OnlineExponentialAverage.java    |   62 +
 .../mahout/math/stats/OnlineSummarizer.java     |   93 +
 .../apache/mahout/math/QRDecompositionTest.java |  280 +++
 .../math/TestSingularValueDecomposition.java    |  327 +++
 .../als/AlternatingLeastSquaresSolverTest.java  |  151 ++
 .../mahout/math/decomposer/SolverTest.java      |  177 ++
 .../decomposer/hebbian/TestHebbianSolver.java   |  207 ++
 .../decomposer/lanczos/TestLanczosSolver.java   |   97 +
 .../apache/mahout/math/jet/stat/GammaTest.java  |  138 ++
 .../mahout/math/jet/stat/ProbabilityTest.java   |  196 ++
 .../math/random/ChineseRestaurantTest.java      |  158 ++
 .../mahout/math/randomized/RandomBlasting.java  |  355 +++
 .../mahout/math/ssvd/SequentialBigSvdTest.java  |   86 +
 .../mahout/math/stats/OnlineSummarizerTest.java |  108 +
 154 files changed, 32350 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/collections/Arithmetic.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/collections/Arithmetic.java b/core/src/main/java/org/apache/mahout/collections/Arithmetic.java
new file mode 100644
index 0000000..18e3200
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/collections/Arithmetic.java
@@ -0,0 +1,489 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+Copyright 1999 CERN - European Organization for Nuclear Research.
+Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose 
+is hereby granted without fee, provided that the above copyright notice appear in all copies and 
+that both that copyright notice and this permission notice appear in supporting documentation. 
+CERN makes no representations about the suitability of this software for any purpose. 
+It is provided "as is" without expressed or implied warranty.
+*/
+package org.apache.mahout.collections;
+
+/**
+ * Arithmetic functions.
+ */
+public final class Arithmetic extends Constants {
+  // for method STIRLING_CORRECTION(...)
+  private static final double[] STIRLING_CORRECTION = {
+    0.0,
+    8.106146679532726e-02, 4.134069595540929e-02,
+    2.767792568499834e-02, 2.079067210376509e-02,
+    1.664469118982119e-02, 1.387612882307075e-02,
+    1.189670994589177e-02, 1.041126526197209e-02,
+    9.255462182712733e-03, 8.330563433362871e-03,
+    7.573675487951841e-03, 6.942840107209530e-03,
+    6.408994188004207e-03, 5.951370112758848e-03,
+    5.554733551962801e-03, 5.207655919609640e-03,
+    4.901395948434738e-03, 4.629153749334029e-03,
+    4.385560249232324e-03, 4.166319691996922e-03,
+    3.967954218640860e-03, 3.787618068444430e-03,
+    3.622960224683090e-03, 3.472021382978770e-03,
+    3.333155636728090e-03, 3.204970228055040e-03,
+    3.086278682608780e-03, 2.976063983550410e-03,
+    2.873449362352470e-03, 2.777674929752690e-03,
+  };
+
+  // for method logFactorial(...)
+  // log(k!) for k = 0, ..., 29
+  private static final double[] LOG_FACTORIALS = {
+    0.00000000000000000, 0.00000000000000000, 0.69314718055994531,
+    1.79175946922805500, 3.17805383034794562, 4.78749174278204599,
+    6.57925121201010100, 8.52516136106541430, 10.60460290274525023,
+    12.80182748008146961, 15.10441257307551530, 17.50230784587388584,
+    19.98721449566188615, 22.55216385312342289, 25.19122118273868150,
+    27.89927138384089157, 30.67186010608067280, 33.50507345013688888,
+    36.39544520803305358, 39.33988418719949404, 42.33561646075348503,
+    45.38013889847690803, 48.47118135183522388, 51.60667556776437357,
+    54.78472939811231919, 58.00360522298051994, 61.26170176100200198,
+    64.55753862700633106, 67.88974313718153498, 71.25703896716800901
+  };
+
+  // k! for k = 0, ..., 20
+  private static final long[] LONG_FACTORIALS = {
+    1L,
+    1L,
+    2L,
+    6L,
+    24L,
+    120L,
+    720L,
+    5040L,
+    40320L,
+    362880L,
+    3628800L,
+    39916800L,
+    479001600L,
+    6227020800L,
+    87178291200L,
+    1307674368000L,
+    20922789888000L,
+    355687428096000L,
+    6402373705728000L,
+    121645100408832000L,
+    2432902008176640000L
+  };
+
+  // k! for k = 21, ..., 170
+  private static final double[] DOUBLE_FACTORIALS = {
+    5.109094217170944E19,
+    1.1240007277776077E21,
+    2.585201673888498E22,
+    6.204484017332394E23,
+    1.5511210043330984E25,
+    4.032914611266057E26,
+    1.0888869450418352E28,
+    3.048883446117138E29,
+    8.841761993739701E30,
+    2.652528598121911E32,
+    8.222838654177924E33,
+    2.6313083693369355E35,
+    8.68331761881189E36,
+    2.952327990396041E38,
+    1.0333147966386144E40,
+    3.719933267899013E41,
+    1.3763753091226346E43,
+    5.23022617466601E44,
+    2.0397882081197447E46,
+    8.15915283247898E47,
+    3.34525266131638E49,
+    1.4050061177528801E51,
+    6.041526306337384E52,
+    2.6582715747884495E54,
+    1.196222208654802E56,
+    5.502622159812089E57,
+    2.5862324151116827E59,
+    1.2413915592536068E61,
+    6.082818640342679E62,
+    3.0414093201713376E64,
+    1.5511187532873816E66,
+    8.06581751709439E67,
+    4.274883284060024E69,
+    2.308436973392413E71,
+    1.2696403353658264E73,
+    7.109985878048632E74,
+    4.052691950487723E76,
+    2.350561331282879E78,
+    1.386831185456898E80,
+    8.32098711274139E81,
+    5.075802138772246E83,
+    3.146997326038794E85,
+    1.9826083154044396E87,
+    1.2688693218588414E89,
+    8.247650592082472E90,
+    5.443449390774432E92,
+    3.6471110918188705E94,
+    2.48003554243683E96,
+    1.7112245242814127E98,
+    1.1978571669969892E100,
+    8.504785885678624E101,
+    6.123445837688612E103,
+    4.470115461512686E105,
+    3.307885441519387E107,
+    2.4809140811395404E109,
+    1.8854947016660506E111,
+    1.451830920282859E113,
+    1.1324281178206295E115,
+    8.94618213078298E116,
+    7.15694570462638E118,
+    5.797126020747369E120,
+    4.7536433370128435E122,
+    3.94552396972066E124,
+    3.314240134565354E126,
+    2.8171041143805494E128,
+    2.4227095383672744E130,
+    2.107757298379527E132,
+    1.854826422573984E134,
+    1.6507955160908465E136,
+    1.4857159644817605E138,
+    1.3520015276784033E140,
+    1.2438414054641305E142,
+    1.156772507081641E144,
+    1.0873661566567426E146,
+    1.0329978488239061E148,
+    9.916779348709491E149,
+    9.619275968248216E151,
+    9.426890448883248E153,
+    9.332621544394415E155,
+    9.332621544394418E157,
+    9.42594775983836E159,
+    9.614466715035125E161,
+    9.902900716486178E163,
+    1.0299016745145631E166,
+    1.0813967582402912E168,
+    1.1462805637347086E170,
+    1.2265202031961373E172,
+    1.324641819451829E174,
+    1.4438595832024942E176,
+    1.5882455415227423E178,
+    1.7629525510902457E180,
+    1.974506857221075E182,
+    2.2311927486598138E184,
+    2.543559733472186E186,
+    2.925093693493014E188,
+    3.393108684451899E190,
+    3.96993716080872E192,
+    4.6845258497542896E194,
+    5.574585761207606E196,
+    6.689502913449135E198,
+    8.094298525273444E200,
+    9.875044200833601E202,
+    1.2146304367025332E205,
+    1.506141741511141E207,
+    1.882677176888926E209,
+    2.3721732428800483E211,
+    3.0126600184576624E213,
+    3.856204823625808E215,
+    4.974504222477287E217,
+    6.466855489220473E219,
+    8.471580690878813E221,
+    1.1182486511960037E224,
+    1.4872707060906847E226,
+    1.99294274616152E228,
+    2.690472707318049E230,
+    3.6590428819525483E232,
+    5.0128887482749884E234,
+    6.917786472619482E236,
+    9.615723196941089E238,
+    1.3462012475717523E241,
+    1.8981437590761713E243,
+    2.6953641378881633E245,
+    3.8543707171800694E247,
+    5.550293832739308E249,
+    8.047926057471989E251,
+    1.1749972043909107E254,
+    1.72724589045464E256,
+    2.5563239178728637E258,
+    3.8089226376305687E260,
+    5.7133839564458575E262,
+    8.627209774233244E264,
+    1.3113358856834527E267,
+    2.0063439050956838E269,
+    3.0897696138473515E271,
+    4.789142901463393E273,
+    7.471062926282892E275,
+    1.1729568794264134E278,
+    1.8532718694937346E280,
+    2.946702272495036E282,
+    4.714723635992061E284,
+    7.590705053947223E286,
+    1.2296942187394494E289,
+    2.0044015765453032E291,
+    3.287218585534299E293,
+    5.423910666131583E295,
+    9.003691705778434E297,
+    1.5036165148649983E300,
+    2.5260757449731988E302,
+    4.2690680090047056E304,
+    7.257415615308004E306
+  };
+
+  /** Makes this class non instantiable, but still let's others inherit from it. */
+  Arithmetic() {
+  }
+
+  /**
+   * Efficiently returns the binomial coefficient, often also referred to as 
+   * "n over k" or "n choose k". The binomial coefficient is defined as 
+   * <tt>(n * n-1 * ... * n-k+1 ) / ( 1 * 2 * ... * k )</tt>.
+   * <ul> <li><tt>k&lt;0</tt>: <tt>0</tt>.</li>
+   * <li><tt>k==0</tt>: <tt>1</tt>.</li>
+   * <li><tt>k==1</tt>: <tt>n</tt>.</li>
+   * <li>else: <tt>(n * n-1 * ... * n-k+1 ) / ( 1 * 2 * ... * k)</tt>.</li>
+   * </ul>
+   *
+     * @param n
+     * @param k
+   * @return the binomial coefficient.
+   */
+  public static double binomial(double n, long k) {
+    if (k < 0) {
+      return 0;
+    }
+    if (k == 0) {
+      return 1;
+    }
+    if (k == 1) {
+      return n;
+    }
+
+    // binomial(n,k) = (n * n-1 * ... * n-k+1 ) / ( 1 * 2 * ... * k )
+    double a = n - k + 1;
+    double b = 1;
+    double binomial = 1;
+    for (long i = k; i-- > 0;) {
+      binomial *= (a++) / (b++);
+    }
+    return binomial;
+  }
+
+  /**
+   * Efficiently returns the binomial coefficient, often also referred to as "n over k" or "n choose k". The binomial
+   * coefficient is defined as <ul> <li><tt>k&lt;0</tt>: <tt>0</tt>. <li><tt>k==0 || k==n</tt>: <tt>1</tt>. <li><tt>k==1 || k==n-1</tt>:
+   * <tt>n</tt>. <li>else: <tt>(n * n-1 * ... * n-k+1 ) / ( 1 * 2 * ... * k )</tt>. </ul>
+   *
+   * @return the binomial coefficient.
+   */
+  public static double binomial(long n, long k) {
+    if (k < 0) {
+      return 0;
+    }
+    if (k == 0 || k == n) {
+      return 1;
+    }
+    if (k == 1 || k == n - 1) {
+      return n;
+    }
+
+    // try quick version and see whether we get numeric overflows.
+    // factorial(..) is O(1); requires no loop; only a table lookup.
+    if (n > k) {
+      int max = LONG_FACTORIALS.length + DOUBLE_FACTORIALS.length;
+      if (n < max) { // if (n! < inf && k! < inf)
+        double n_fac = factorial((int) n);
+        double k_fac = factorial((int) k);
+        double n_minus_k_fac = factorial((int) (n - k));
+        double nk = n_minus_k_fac * k_fac;
+        if (nk != Double.POSITIVE_INFINITY) { // no numeric overflow?
+          // now this is completely safe and accurate
+          return n_fac / nk;
+        }
+      }
+      if (k > n / 2) {
+        k = n - k;
+      } // quicker
+    }
+
+    // binomial(n,k) = (n * n-1 * ... * n-k+1 ) / ( 1 * 2 * ... * k )
+    long a = n - k + 1;
+    long b = 1;
+    double binomial = 1;
+    for (long i = k; i-- > 0;) {
+      binomial *= (double) a++ / (b++);
+    }
+    return binomial;
+  }
+
+  /**
+   * Returns the smallest <code>long &gt;= value</code>.
+   * <dl><dt>Examples: {@code 1.0 -> 1, 1.2 -> 2, 1.9 -> 2}. This
+   * method is safer than using (long) Math.ceil(value), because of possible rounding error.</dt></dl>
+   */
+  public static long ceil(double value) {
+    return Math.round(Math.ceil(value));
+  }
+
+  /**
+   * Evaluates the series of Chebyshev polynomials Ti at argument x/2. The series is given by
+   * <pre>
+   *        N-1
+   *         - '
+   *  y  =   &gt;   coef[i] T (x/2)
+   *         -            i
+   *        i=0
+   * </pre>
+   * Coefficients are stored in reverse order, i.e. the zero order term is last in the array.  Note N is the number of
+   * coefficients, not the order. <p> If coefficients are for the interval a to b, x must have been transformed to x -&lt;
+   * 2(2x - b - a)/(b-a) before entering the routine.  This maps x from (a, b) to (-1, 1), over which the Chebyshev
+   * polynomials are defined. <p> If the coefficients are for the inverted interval, in which (a, b) is mapped to (1/b,
+   * 1/a), the transformation required is {@code x -> 2(2ab/x - b - a)/(b-a)}.  If b is infinity, this becomes {@code x -> 4a/x - 1}.
+   * <p> SPEED: <p> Taking advantage of the recurrence properties of the Chebyshev polynomials, the routine requires one
+   * more addition per loop than evaluating a nested polynomial of the same degree.
+   *
+   * @param x    argument to the polynomial.
+   * @param coef the coefficients of the polynomial.
+   * @param N    the number of coefficients.
+   */
+  public static double chbevl(double x, double[] coef, int N) {
+
+    int p = 0;
+
+    double b0 = coef[p++];
+    double b1 = 0.0;
+    int i = N - 1;
+
+    double b2;
+    do {
+      b2 = b1;
+      b1 = b0;
+      b0 = x * b1 - b2 + coef[p++];
+    } while (--i > 0);
+
+    return 0.5 * (b0 - b2);
+  }
+
+  /**
+   * Instantly returns the factorial <tt>k!</tt>.
+   *
+   * @param k must hold <tt>k &gt;= 0</tt>.
+   */
+  private static double factorial(int k) {
+    if (k < 0) {
+      throw new IllegalArgumentException();
+    }
+
+    int length1 = LONG_FACTORIALS.length;
+    if (k < length1) {
+      return LONG_FACTORIALS[k];
+    }
+
+    int length2 = DOUBLE_FACTORIALS.length;
+    if (k < length1 + length2) {
+      return DOUBLE_FACTORIALS[k - length1];
+    } else {
+      return Double.POSITIVE_INFINITY;
+    }
+  }
+
+  /**
+   * Returns the largest <code>long &lt;= value</code>.
+   * <dl><dt>Examples: {@code 1.0 -> 1, 1.2 -> 1, 1.9 -> 1 <dt> 2.0 -> 2, 2.2 -> 2, 2.9 -> 2}</dt></dl>
+   * This method is safer than using (long) Math.floor(value), because of possible rounding error.
+   */
+  public static long floor(double value) {
+    return Math.round(Math.floor(value));
+  }
+
+  /** Returns <tt>log<sub>base</sub>value</tt>. */
+  public static double log(double base, double value) {
+    return Math.log(value) / Math.log(base);
+  }
+
+  /** Returns <tt>log<sub>10</sub>value</tt>. */
+  public static double log10(double value) {
+    // 1.0 / Math.log(10) == 0.43429448190325176
+    return Math.log(value) * 0.43429448190325176;
+  }
+
+  /** Returns <tt>log<sub>2</sub>value</tt>. */
+  public static double log2(double value) {
+    // 1.0 / Math.log(2) == 1.4426950408889634
+    return Math.log(value) * 1.4426950408889634;
+  }
+
+  /**
+   * Returns <tt>log(k!)</tt>. Tries to avoid overflows. For <tt>k&lt;30</tt> simply looks up a table in O(1). For
+   * <tt>k&gt;=30</tt> uses stirlings approximation.
+   *
+   * @param k must hold <tt>k &gt;= 0</tt>.
+   */
+  public static double logFactorial(int k) {
+    if (k >= 30) {
+
+      double r = 1.0 / k;
+      double rr = r * r;
+      double C7 = -5.95238095238095238e-04;
+      double C5 = 7.93650793650793651e-04;
+      double C3 = -2.77777777777777778e-03;
+      double C1 = 8.33333333333333333e-02;
+      double C0 = 9.18938533204672742e-01;
+      return (k + 0.5) * Math.log(k) - k + C0 + r * (C1 + rr * (C3 + rr * (C5 + rr * C7)));
+    } else {
+      return LOG_FACTORIALS[k];
+    }
+  }
+
+  /**
+   * Instantly returns the factorial <tt>k!</tt>.
+   *
+   * @param k must hold {@code k >= 0 && k < 21}
+   */
+  public static long longFactorial(int k) {
+    if (k < 0) {
+      throw new IllegalArgumentException("Negative k");
+    }
+
+    if (k < LONG_FACTORIALS.length) {
+      return LONG_FACTORIALS[k];
+    }
+    throw new IllegalArgumentException("Overflow");
+  }
+
+  /**
+   * Returns the StirlingCorrection. <p> Correction term of the Stirling approximation for <tt>log(k!)</tt> (series in
+   * 1/k, or table values for small k) with int parameter k. </p> <tt> log k! = (k + 1/2)log(k + 1) - (k + 1) +
+   * (1/2)log(2Pi) + STIRLING_CORRECTION(k + 1) log k! = (k + 1/2)log(k)     -  k      + (1/2)log(2Pi) +
+   * STIRLING_CORRECTION(k) </tt>
+   */
+  public static double stirlingCorrection(int k) {
+
+    if (k > 30) {
+      double r = 1.0 / k;
+      double rr = r * r;
+      double C7 = -5.95238095238095238e-04;     //  -1/1680
+      double C5 = 7.93650793650793651e-04;     //  +1/1260
+      double C3 = -2.77777777777777778e-03;     //  -1/360
+      double C1 = 8.33333333333333333e-02;     //  +1/12
+      return r * (C1 + rr * (C3 + rr * (C5 + rr * C7)));
+    } else {
+      return STIRLING_CORRECTION[k];
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/collections/Constants.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/collections/Constants.java b/core/src/main/java/org/apache/mahout/collections/Constants.java
new file mode 100644
index 0000000..007bd3f
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/collections/Constants.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+Copyright 1999 CERN - European Organization for Nuclear Research.
+Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose 
+is hereby granted without fee, provided that the above copyright notice appear in all copies and 
+that both that copyright notice and this permission notice appear in supporting documentation. 
+CERN makes no representations about the suitability of this software for any purpose. 
+It is provided "as is" without expressed or implied warranty.
+*/
+package org.apache.mahout.collections;
+
+/**
+ * Defines some useful constants.
+ */
+public class Constants {
+  /*
+   * machine constants
+   */
+  protected static final double MACHEP = 1.11022302462515654042E-16;
+  protected static final double MAXLOG = 7.09782712893383996732E2;
+  protected static final double MINLOG = -7.451332191019412076235E2;
+  protected static final double MAXGAM = 171.624376956302725;
+  protected static final double SQTPI = 2.50662827463100050242E0;
+  protected static final double SQRTH = 7.07106781186547524401E-1;
+  protected static final double LOGPI = 1.14472988584940017414;
+
+  protected static final double BIG = 4.503599627370496e15;
+  protected static final double BIGINV = 2.22044604925031308085e-16;
+
+
+  /*
+ * MACHEP =  1.38777878078144567553E-17       2**-56
+ * MAXLOG =  8.8029691931113054295988E1       log(2**127)
+ * MINLOG = -8.872283911167299960540E1        log(2**-128)
+ * MAXNUM =  1.701411834604692317316873e38    2**127
+ *
+ * For IEEE arithmetic (IBMPC):
+ * MACHEP =  1.11022302462515654042E-16       2**-53
+ * MAXLOG =  7.09782712893383996843E2         log(2**1024)
+ * MINLOG = -7.08396418532264106224E2         log(2**-1022)
+ * MAXNUM =  1.7976931348623158E308           2**1024
+ *
+ * The global symbols for mathematical constants are
+ * PI     =  3.14159265358979323846           pi
+ * PIO2   =  1.57079632679489661923           pi/2
+ * PIO4   =  7.85398163397448309616E-1        pi/4
+ * SQRT2  =  1.41421356237309504880           sqrt(2)
+ * SQRTH  =  7.07106781186547524401E-1        sqrt(2)/2
+ * LOG2E  =  1.4426950408889634073599         1/log(2)
+ * SQ2OPI =  7.9788456080286535587989E-1      sqrt( 2/pi )
+ * LOGE2  =  6.93147180559945309417E-1        log(2)
+ * LOGSQ2 =  3.46573590279972654709E-1        log(2)/2
+ * THPIO4 =  2.35619449019234492885           3*pi/4
+ * TWOOPI =  6.36619772367581343075535E-1     2/pi
+ */
+  protected Constants() {}
+}

http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/common/RandomUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/common/RandomUtils.java b/core/src/main/java/org/apache/mahout/common/RandomUtils.java
new file mode 100644
index 0000000..ba71292
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/common/RandomUtils.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.common;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.WeakHashMap;
+
+import com.google.common.primitives.Longs;
+import org.apache.commons.math3.primes.Primes;
+
+/**
+ * <p>
+ * The source of random stuff for the whole project. This lets us make all randomness in the project
+ * predictable, if desired, for when we run unit tests, which should be repeatable.
+ * </p>
+ */
+public final class RandomUtils {
+
+  /** The largest prime less than 2<sup>31</sup>-1 that is the smaller of a twin prime pair. */
+  public static final int MAX_INT_SMALLER_TWIN_PRIME = 2147482949;
+
+  private static final Map<RandomWrapper,Boolean> INSTANCES =
+      Collections.synchronizedMap(new WeakHashMap<RandomWrapper,Boolean>());
+
+  private static boolean testSeed = false;
+
+  private RandomUtils() { }
+  
+  public static void useTestSeed() {
+    testSeed = true;
+    synchronized (INSTANCES) {
+      for (RandomWrapper rng : INSTANCES.keySet()) {
+        rng.resetToTestSeed();
+      }
+    }
+  }
+  
+  public static RandomWrapper getRandom() {
+    RandomWrapper random = new RandomWrapper();
+    if (testSeed) {
+      random.resetToTestSeed();
+    }
+    INSTANCES.put(random, Boolean.TRUE);
+    return random;
+  }
+  
+  public static Random getRandom(long seed) {
+    RandomWrapper random = new RandomWrapper(seed);
+    INSTANCES.put(random, Boolean.TRUE);
+    return random;
+  }
+  
+  /** @return what {@link Double#hashCode()} would return for the same value */
+  public static int hashDouble(double value) {
+    return Longs.hashCode(Double.doubleToLongBits(value));
+  }
+
+  /** @return what {@link Float#hashCode()} would return for the same value */
+  public static int hashFloat(float value) {
+    return Float.floatToIntBits(value);
+  }
+  
+  /**
+   * <p>
+   * Finds next-largest "twin primes": numbers p and p+2 such that both are prime. Finds the smallest such p
+   * such that the smaller twin, p, is greater than or equal to n. Returns p+2, the larger of the two twins.
+   * </p>
+   */
+  public static int nextTwinPrime(int n) {
+    if (n > MAX_INT_SMALLER_TWIN_PRIME) {
+      throw new IllegalArgumentException();
+    }
+    if (n <= 3) {
+      return 5;
+    }
+    int next = Primes.nextPrime(n);
+    while (!Primes.isPrime(next + 2)) {
+      next = Primes.nextPrime(next + 4);
+    }
+    return next + 2;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/common/RandomWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/common/RandomWrapper.java b/core/src/main/java/org/apache/mahout/common/RandomWrapper.java
new file mode 100644
index 0000000..802291b
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/common/RandomWrapper.java
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.common;
+
+import org.apache.commons.math3.random.MersenneTwister;
+import org.apache.commons.math3.random.RandomGenerator;
+
+import java.util.Random;
+
+public final class RandomWrapper extends Random {
+
+  private static final long STANDARD_SEED = 0xCAFEDEADBEEFBABEL;
+
+  private final RandomGenerator random;
+
+  RandomWrapper() {
+    random = new MersenneTwister();
+    random.setSeed(System.currentTimeMillis() + System.identityHashCode(random));
+  }
+
+  RandomWrapper(long seed) {
+    random = new MersenneTwister(seed);
+  }
+
+  @Override
+  public void setSeed(long seed) {
+    // Since this will be called by the java.util.Random() constructor before we construct
+    // the delegate... and because we don't actually care about the result of this for our
+    // purpose:
+    if (random != null) {
+      random.setSeed(seed);
+    }
+  }
+
+  void resetToTestSeed() {
+    setSeed(STANDARD_SEED);
+  }
+
+  public RandomGenerator getRandomGenerator() {
+    return random;
+  }
+
+  @Override
+  protected int next(int bits) {
+    // Ugh, can't delegate this method -- it's protected
+    // Callers can't use it and other methods are delegated, so shouldn't matter
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void nextBytes(byte[] bytes) {
+    random.nextBytes(bytes);
+  }
+
+  @Override
+  public int nextInt() {
+    return random.nextInt();
+  }
+
+  @Override
+  public int nextInt(int n) {
+    return random.nextInt(n);
+  }
+
+  @Override
+  public long nextLong() {
+    return random.nextLong();
+  }
+
+  @Override
+  public boolean nextBoolean() {
+    return random.nextBoolean();
+  }
+
+  @Override
+  public float nextFloat() {
+    return random.nextFloat();
+  }
+
+  @Override
+  public double nextDouble() {
+    return random.nextDouble();
+  }
+
+  @Override
+  public double nextGaussian() {
+    return random.nextGaussian();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/math/AbstractMatrix.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/math/AbstractMatrix.java b/core/src/main/java/org/apache/mahout/math/AbstractMatrix.java
new file mode 100644
index 0000000..eaaa397
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/math/AbstractMatrix.java
@@ -0,0 +1,834 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.math;
+
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.Maps;
+import org.apache.mahout.math.flavor.MatrixFlavor;
+import org.apache.mahout.math.function.DoubleDoubleFunction;
+import org.apache.mahout.math.function.DoubleFunction;
+import org.apache.mahout.math.function.Functions;
+import org.apache.mahout.math.function.PlusMult;
+import org.apache.mahout.math.function.VectorFunction;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A few universal implementations of convenience functions for a JVM-backed matrix.
+ */
+public abstract class AbstractMatrix implements Matrix {
+
+  protected Map<String, Integer> columnLabelBindings;
+  protected Map<String, Integer> rowLabelBindings;
+  protected int rows;
+  protected int columns;
+
+  protected AbstractMatrix(int rows, int columns) {
+    this.rows = rows;
+    this.columns = columns;
+  }
+
+  @Override
+  public int columnSize() {
+    return columns;
+  }
+
+  @Override
+  public int rowSize() {
+    return rows;
+  }
+
+  @Override
+  public Iterator<MatrixSlice> iterator() {
+    return iterateAll();
+  }
+
+  @Override
+  public Iterator<MatrixSlice> iterateAll() {
+    return new AbstractIterator<MatrixSlice>() {
+      private int row;
+
+      @Override
+      protected MatrixSlice computeNext() {
+        if (row >= numRows()) {
+          return endOfData();
+        }
+        int i = row++;
+        return new MatrixSlice(viewRow(i), i);
+      }
+    };
+  }
+
+  @Override
+  public Iterator<MatrixSlice> iterateNonEmpty() {
+    return iterator();
+  }
+
+  /**
+   * Abstracted out for the iterator
+   *
+   * @return numRows() for row-based iterator, numColumns() for column-based.
+   */
+  @Override
+  public int numSlices() {
+    return numRows();
+  }
+
+  @Override
+  public double get(String rowLabel, String columnLabel) {
+    if (columnLabelBindings == null || rowLabelBindings == null) {
+      throw new IllegalStateException("Unbound label");
+    }
+    Integer row = rowLabelBindings.get(rowLabel);
+    Integer col = columnLabelBindings.get(columnLabel);
+    if (row == null || col == null) {
+      throw new IllegalStateException("Unbound label");
+    }
+
+    return get(row, col);
+  }
+
+  @Override
+  public Map<String, Integer> getColumnLabelBindings() {
+    return columnLabelBindings;
+  }
+
+  @Override
+  public Map<String, Integer> getRowLabelBindings() {
+    return rowLabelBindings;
+  }
+
+  @Override
+  public void set(String rowLabel, double[] rowData) {
+    if (columnLabelBindings == null) {
+      throw new IllegalStateException("Unbound label");
+    }
+    Integer row = rowLabelBindings.get(rowLabel);
+    if (row == null) {
+      throw new IllegalStateException("Unbound label");
+    }
+    set(row, rowData);
+  }
+
+  @Override
+  public void set(String rowLabel, int row, double[] rowData) {
+    if (rowLabelBindings == null) {
+      rowLabelBindings = new HashMap<>();
+    }
+    rowLabelBindings.put(rowLabel, row);
+    set(row, rowData);
+  }
+
+  @Override
+  public void set(String rowLabel, String columnLabel, double value) {
+    if (columnLabelBindings == null || rowLabelBindings == null) {
+      throw new IllegalStateException("Unbound label");
+    }
+    Integer row = rowLabelBindings.get(rowLabel);
+    Integer col = columnLabelBindings.get(columnLabel);
+    if (row == null || col == null) {
+      throw new IllegalStateException("Unbound label");
+    }
+    set(row, col, value);
+  }
+
+  @Override
+  public void set(String rowLabel, String columnLabel, int row, int column, double value) {
+    if (rowLabelBindings == null) {
+      rowLabelBindings = new HashMap<>();
+    }
+    rowLabelBindings.put(rowLabel, row);
+    if (columnLabelBindings == null) {
+      columnLabelBindings = new HashMap<>();
+    }
+    columnLabelBindings.put(columnLabel, column);
+
+    set(row, column, value);
+  }
+
+  @Override
+  public void setColumnLabelBindings(Map<String, Integer> bindings) {
+    columnLabelBindings = bindings;
+  }
+
+  @Override
+  public void setRowLabelBindings(Map<String, Integer> bindings) {
+    rowLabelBindings = bindings;
+  }
+
+  // index into int[2] for column value
+  public static final int COL = 1;
+
+  // index into int[2] for row value
+  public static final int ROW = 0;
+
+  @Override
+  public int numRows() {
+    return rowSize();
+  }
+
+  @Override
+  public int numCols() {
+    return columnSize();
+  }
+
+  @Override
+  public String asFormatString() {
+    return toString();
+  }
+
+  @Override
+  public Matrix assign(double value) {
+    int rows = rowSize();
+    int columns = columnSize();
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        setQuick(row, col, value);
+      }
+    }
+    return this;
+  }
+
+  @Override
+  public Matrix assign(double[][] values) {
+    int rows = rowSize();
+    if (rows != values.length) {
+      throw new CardinalityException(rows, values.length);
+    }
+    int columns = columnSize();
+    for (int row = 0; row < rows; row++) {
+      if (columns == values[row].length) {
+        for (int col = 0; col < columns; col++) {
+          setQuick(row, col, values[row][col]);
+        }
+      } else {
+        throw new CardinalityException(columns, values[row].length);
+      }
+    }
+    return this;
+  }
+
+  @Override
+  public Matrix assign(Matrix other, DoubleDoubleFunction function) {
+    int rows = rowSize();
+    if (rows != other.rowSize()) {
+      throw new CardinalityException(rows, other.rowSize());
+    }
+    int columns = columnSize();
+    if (columns != other.columnSize()) {
+      throw new CardinalityException(columns, other.columnSize());
+    }
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        setQuick(row, col, function.apply(getQuick(row, col), other.getQuick(
+          row, col)));
+      }
+    }
+    return this;
+  }
+
+  @Override
+  public Matrix assign(Matrix other) {
+    int rows = rowSize();
+    if (rows != other.rowSize()) {
+      throw new CardinalityException(rows, other.rowSize());
+    }
+    int columns = columnSize();
+    if (columns != other.columnSize()) {
+      throw new CardinalityException(columns, other.columnSize());
+    }
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        setQuick(row, col, other.getQuick(row, col));
+      }
+    }
+    return this;
+  }
+
+  @Override
+  public Matrix assign(DoubleFunction function) {
+    int rows = rowSize();
+    int columns = columnSize();
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        setQuick(row, col, function.apply(getQuick(row, col)));
+      }
+    }
+    return this;
+  }
+
+  /**
+   * Collects the results of a function applied to each row of a matrix.
+   *
+   * @param f The function to be applied to each row.
+   * @return The vector of results.
+   */
+  @Override
+  public Vector aggregateRows(VectorFunction f) {
+    Vector r = new DenseVector(numRows());
+    int n = numRows();
+    for (int row = 0; row < n; row++) {
+      r.set(row, f.apply(viewRow(row)));
+    }
+    return r;
+  }
+
+  /**
+   * Returns a view of a row.  Changes to the view will affect the original.
+   *
+   * @param row Which row to return.
+   * @return A vector that references the desired row.
+   */
+  @Override
+  public Vector viewRow(int row) {
+    return new MatrixVectorView(this, row, 0, 0, 1);
+  }
+
+
+  /**
+   * Returns a view of a row.  Changes to the view will affect the original.
+   *
+   * @param column Which column to return.
+   * @return A vector that references the desired column.
+   */
+  @Override
+  public Vector viewColumn(int column) {
+    return new MatrixVectorView(this, 0, column, 1, 0);
+  }
+
+  /**
+   * Provides a view of the diagonal of a matrix.
+   */
+  @Override
+  public Vector viewDiagonal() {
+    return new MatrixVectorView(this, 0, 0, 1, 1);
+  }
+
+  /**
+   * Collects the results of a function applied to each element of a matrix and then aggregated.
+   *
+   * @param combiner A function that combines the results of the mapper.
+   * @param mapper   A function to apply to each element.
+   * @return The result.
+   */
+  @Override
+  public double aggregate(final DoubleDoubleFunction combiner, final DoubleFunction mapper) {
+    return aggregateRows(new VectorFunction() {
+      @Override
+      public double apply(Vector v) {
+        return v.aggregate(combiner, mapper);
+      }
+    }).aggregate(combiner, Functions.IDENTITY);
+  }
+
+  /**
+   * Collects the results of a function applied to each column of a matrix.
+   *
+   * @param f The function to be applied to each column.
+   * @return The vector of results.
+   */
+  @Override
+  public Vector aggregateColumns(VectorFunction f) {
+    Vector r = new DenseVector(numCols());
+    for (int col = 0; col < numCols(); col++) {
+      r.set(col, f.apply(viewColumn(col)));
+    }
+    return r;
+  }
+
+  @Override
+  public double determinant() {
+    int rows = rowSize();
+    int columns = columnSize();
+    if (rows != columns) {
+      throw new CardinalityException(rows, columns);
+    }
+
+    if (rows == 2) {
+      return getQuick(0, 0) * getQuick(1, 1) - getQuick(0, 1) * getQuick(1, 0);
+    } else {
+      // TODO: this really should just be one line:
+      // TODO: new CholeskyDecomposition(this).getL().viewDiagonal().aggregate(Functions.TIMES)
+      int sign = 1;
+      double ret = 0;
+
+      for (int i = 0; i < columns; i++) {
+        Matrix minor = new DenseMatrix(rows - 1, columns - 1);
+        for (int j = 1; j < rows; j++) {
+          boolean flag = false; /* column offset flag */
+          for (int k = 0; k < columns; k++) {
+            if (k == i) {
+              flag = true;
+              continue;
+            }
+            minor.set(j - 1, flag ? k - 1 : k, getQuick(j, k));
+          }
+        }
+        ret += getQuick(0, i) * sign * minor.determinant();
+        sign *= -1;
+
+      }
+
+      return ret;
+    }
+
+  }
+
+  @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
+  @Override
+  public Matrix clone() {
+    AbstractMatrix clone;
+    try {
+      clone = (AbstractMatrix) super.clone();
+    } catch (CloneNotSupportedException cnse) {
+      throw new IllegalStateException(cnse); // can't happen
+    }
+    if (rowLabelBindings != null) {
+      clone.rowLabelBindings = Maps.newHashMap(rowLabelBindings);
+    }
+    if (columnLabelBindings != null) {
+      clone.columnLabelBindings = Maps.newHashMap(columnLabelBindings);
+    }
+    return clone;
+  }
+
+  @Override
+  public Matrix divide(double x) {
+    Matrix result = like();
+    for (int row = 0; row < rowSize(); row++) {
+      for (int col = 0; col < columnSize(); col++) {
+        result.setQuick(row, col, getQuick(row, col) / x);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public double get(int row, int column) {
+    if (row < 0 || row >= rowSize()) {
+      throw new IndexException(row, rowSize());
+    }
+    if (column < 0 || column >= columnSize()) {
+      throw new IndexException(column, columnSize());
+    }
+    return getQuick(row, column);
+  }
+
+  @Override
+  public Matrix minus(Matrix other) {
+    int rows = rowSize();
+    if (rows != other.rowSize()) {
+      throw new CardinalityException(rows, other.rowSize());
+    }
+    int columns = columnSize();
+    if (columns != other.columnSize()) {
+      throw new CardinalityException(columns, other.columnSize());
+    }
+    Matrix result = like();
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        result.setQuick(row, col, getQuick(row, col)
+          - other.getQuick(row, col));
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public Matrix plus(double x) {
+    Matrix result = like();
+    int rows = rowSize();
+    int columns = columnSize();
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        result.setQuick(row, col, getQuick(row, col) + x);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public Matrix plus(Matrix other) {
+    int rows = rowSize();
+    if (rows != other.rowSize()) {
+      throw new CardinalityException(rows, other.rowSize());
+    }
+    int columns = columnSize();
+    if (columns != other.columnSize()) {
+      throw new CardinalityException(columns, other.columnSize());
+    }
+    Matrix result = like();
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        result.setQuick(row, col, getQuick(row, col)
+          + other.getQuick(row, col));
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public void set(int row, int column, double value) {
+    if (row < 0 || row >= rowSize()) {
+      throw new IndexException(row, rowSize());
+    }
+    if (column < 0 || column >= columnSize()) {
+      throw new IndexException(column, columnSize());
+    }
+    setQuick(row, column, value);
+  }
+
+  @Override
+  public void set(int row, double[] data) {
+    int columns = columnSize();
+    if (columns < data.length) {
+      throw new CardinalityException(columns, data.length);
+    }
+    int rows = rowSize();
+    if (row < 0 || row >= rows) {
+      throw new IndexException(row, rowSize());
+    }
+    for (int i = 0; i < columns; i++) {
+      setQuick(row, i, data[i]);
+    }
+  }
+
+  @Override
+  public Matrix times(double x) {
+    Matrix result = like();
+    int rows = rowSize();
+    int columns = columnSize();
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        result.setQuick(row, col, getQuick(row, col) * x);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public Matrix times(Matrix other) {
+    int columns = columnSize();
+    if (columns != other.rowSize()) {
+      throw new CardinalityException(columns, other.rowSize());
+    }
+    int rows = rowSize();
+    int otherColumns = other.columnSize();
+    Matrix result = like(rows, otherColumns);
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < otherColumns; col++) {
+        double sum = 0.0;
+        for (int k = 0; k < columns; k++) {
+          sum += getQuick(row, k) * other.getQuick(k, col);
+        }
+        result.setQuick(row, col, sum);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public Vector times(Vector v) {
+    int columns = columnSize();
+    if (columns != v.size()) {
+      throw new CardinalityException(columns, v.size());
+    }
+    int rows = rowSize();
+    Vector w = new DenseVector(rows);
+    for (int row = 0; row < rows; row++) {
+      w.setQuick(row, v.dot(viewRow(row)));
+    }
+    return w;
+  }
+
+  @Override
+  public Vector timesSquared(Vector v) {
+    int columns = columnSize();
+    if (columns != v.size()) {
+      throw new CardinalityException(columns, v.size());
+    }
+    int rows = rowSize();
+    Vector w = new DenseVector(columns);
+    for (int i = 0; i < rows; i++) {
+      Vector xi = viewRow(i);
+      double d = xi.dot(v);
+      if (d != 0.0) {
+        w.assign(xi, new PlusMult(d));
+      }
+
+    }
+    return w;
+  }
+
+  @Override
+  public Matrix transpose() {
+    int rows = rowSize();
+    int columns = columnSize();
+    Matrix result = like(columns, rows);
+    for (int row = 0; row < rows; row++) {
+      for (int col = 0; col < columns; col++) {
+        result.setQuick(col, row, getQuick(row, col));
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public Matrix viewPart(int rowOffset, int rowsRequested, int columnOffset, int columnsRequested) {
+    return viewPart(new int[]{rowOffset, columnOffset}, new int[]{rowsRequested, columnsRequested});
+  }
+
+  @Override
+  public Matrix viewPart(int[] offset, int[] size) {
+
+    if (offset[ROW] < 0) {
+      throw new IndexException(offset[ROW], 0);
+    }
+    if (offset[ROW] + size[ROW] > rowSize()) {
+      throw new IndexException(offset[ROW] + size[ROW], rowSize());
+    }
+    if (offset[COL] < 0) {
+      throw new IndexException(offset[COL], 0);
+    }
+    if (offset[COL] + size[COL] > columnSize()) {
+      throw new IndexException(offset[COL] + size[COL], columnSize());
+    }
+
+    return new MatrixView(this, offset, size);
+  }
+
+
+  @Override
+  public double zSum() {
+    double result = 0;
+    for (int row = 0; row < rowSize(); row++) {
+      for (int col = 0; col < columnSize(); col++) {
+        result += getQuick(row, col);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public int[] getNumNondefaultElements() {
+    return new int[]{rowSize(), columnSize()};
+  }
+
+  protected static class TransposeViewVector extends AbstractVector {
+
+    private final Matrix matrix;
+    private final int transposeOffset;
+    private final int numCols;
+    private final boolean rowToColumn;
+
+    protected TransposeViewVector(Matrix m, int offset) {
+      this(m, offset, true);
+    }
+
+    protected TransposeViewVector(Matrix m, int offset, boolean rowToColumn) {
+      super(rowToColumn ? m.numRows() : m.numCols());
+      matrix = m;
+      this.transposeOffset = offset;
+      this.rowToColumn = rowToColumn;
+      numCols = rowToColumn ? m.numCols() : m.numRows();
+    }
+
+    @SuppressWarnings("CloneDoesntCallSuperClone")
+    @Override
+    public Vector clone() {
+      Vector v = new DenseVector(size());
+      v.assign(this, Functions.PLUS);
+      return v;
+    }
+
+    @Override
+    public boolean isDense() {
+      return true;
+    }
+
+    @Override
+    public boolean isSequentialAccess() {
+      return true;
+    }
+
+    @Override
+    protected Matrix matrixLike(int rows, int columns) {
+      return matrix.like(rows, columns);
+    }
+
+    @Override
+    public Iterator<Element> iterator() {
+      return new AbstractIterator<Element>() {
+        private int i;
+
+        @Override
+        protected Element computeNext() {
+          if (i >= size()) {
+            return endOfData();
+          }
+          return getElement(i++);
+        }
+      };
+    }
+
+    /**
+     * Currently delegates to {@link #iterator()}.
+     * TODO: This could be optimized to at least skip empty rows if there are many of them.
+     *
+     * @return an iterator (currently dense).
+     */
+    @Override
+    public Iterator<Element> iterateNonZero() {
+      return iterator();
+    }
+
+    @Override
+    public Element getElement(final int i) {
+      return new Element() {
+        @Override
+        public double get() {
+          return getQuick(i);
+        }
+
+        @Override
+        public int index() {
+          return i;
+        }
+
+        @Override
+        public void set(double value) {
+          setQuick(i, value);
+        }
+      };
+    }
+
+    /**
+     * Used internally by assign() to update multiple indices and values at once.
+     * Only really useful for sparse vectors (especially SequentialAccessSparseVector).
+     * <p>
+     * If someone ever adds a new type of sparse vectors, this method must merge (index, value) pairs into the vector.
+     *
+     * @param updates a mapping of indices to values to merge in the vector.
+     */
+    @Override
+    public void mergeUpdates(OrderedIntDoubleMapping updates) {
+      throw new UnsupportedOperationException("Cannot mutate TransposeViewVector");
+    }
+
+    @Override
+    public double getQuick(int index) {
+      Vector v = rowToColumn ? matrix.viewColumn(index) : matrix.viewRow(index);
+      return v == null ? 0.0 : v.getQuick(transposeOffset);
+    }
+
+    @Override
+    public void setQuick(int index, double value) {
+      Vector v = rowToColumn ? matrix.viewColumn(index) : matrix.viewRow(index);
+      if (v == null) {
+        v = newVector(numCols);
+        if (rowToColumn) {
+          matrix.assignColumn(index, v);
+        } else {
+          matrix.assignRow(index, v);
+        }
+      }
+      v.setQuick(transposeOffset, value);
+    }
+
+    protected Vector newVector(int cardinality) {
+      return new DenseVector(cardinality);
+    }
+
+    @Override
+    public Vector like() {
+      return new DenseVector(size());
+    }
+
+    public Vector like(int cardinality) {
+      return new DenseVector(cardinality);
+    }
+
+    /**
+     * TODO: currently I don't know of an efficient way to getVector this value correctly.
+     *
+     * @return the number of nonzero entries
+     */
+    @Override
+    public int getNumNondefaultElements() {
+      return size();
+    }
+
+    @Override
+    public double getLookupCost() {
+      return (rowToColumn ? matrix.viewColumn(0) : matrix.viewRow(0)).getLookupCost();
+    }
+
+    @Override
+    public double getIteratorAdvanceCost() {
+      return (rowToColumn ? matrix.viewColumn(0) : matrix.viewRow(0)).getIteratorAdvanceCost();
+    }
+
+    @Override
+    public boolean isAddConstantTime() {
+      return (rowToColumn ? matrix.viewColumn(0) : matrix.viewRow(0)).isAddConstantTime();
+    }
+  }
+
+  @Override
+  public String toString() {
+    int row = 0;
+    int maxRowsToDisplay = 10;
+    int maxColsToDisplay = 20;
+    int colsToDisplay = maxColsToDisplay;
+
+    if(maxColsToDisplay > columnSize()){
+      colsToDisplay = columnSize();
+    }
+
+
+    StringBuilder s = new StringBuilder("{\n");
+    Iterator<MatrixSlice> it = iterator();
+    while ((it.hasNext()) && (row < maxRowsToDisplay)) {
+      MatrixSlice next = it.next();
+      s.append(" ").append(next.index())
+        .append(" =>\t")
+        .append(new VectorView(next.vector(), 0, colsToDisplay))
+        .append('\n');
+      row ++;
+    }
+    String returnString = s.toString();
+    if (maxColsToDisplay <= columnSize()) {
+      returnString = returnString.replace("}", " ... } ");
+    }
+    if(maxRowsToDisplay <= rowSize())
+      return returnString + ("... }");
+    else{
+      return returnString + ("}");
+    }
+  }
+
+  @Override
+  public MatrixFlavor getFlavor() {
+    throw new UnsupportedOperationException("Flavor support not implemented for this matrix.");
+  }
+
+  ////////////// Matrix flavor trait ///////////////////
+
+}

http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/math/AbstractVector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/math/AbstractVector.java b/core/src/main/java/org/apache/mahout/math/AbstractVector.java
new file mode 100644
index 0000000..27eddbc
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/math/AbstractVector.java
@@ -0,0 +1,684 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.math;
+
+import java.util.Iterator;
+
+import com.google.common.base.Preconditions;
+import org.apache.mahout.common.RandomUtils;
+import org.apache.mahout.math.function.DoubleDoubleFunction;
+import org.apache.mahout.math.function.DoubleFunction;
+import org.apache.mahout.math.function.Functions;
+
+/** Implementations of generic capabilities like sum of elements and dot products */
+public abstract class AbstractVector implements Vector, LengthCachingVector {
+
+  private int size;
+  protected double lengthSquared = -1.0;
+
+  protected AbstractVector(int size) {
+    this.size = size;
+  }
+
+  @Override
+  public Iterable<Element> all() {
+    return new Iterable<Element>() {
+      @Override
+      public Iterator<Element> iterator() {
+        return AbstractVector.this.iterator();
+      }
+    };
+  }
+
+  @Override
+  public Iterable<Element> nonZeroes() {
+    return new Iterable<Element>() {
+      @Override
+      public Iterator<Element> iterator() {
+        return iterateNonZero();
+      }
+    };
+  }
+
+  /**
+   * Iterates over all elements <p>
+   * NOTE: Implementations may choose to reuse the Element returned for performance
+   * reasons, so if you need a copy of it, you should call {@link #getElement(int)} for the given index
+   *
+   * @return An {@link Iterator} over all elements
+   */
+  protected abstract Iterator<Element> iterator();
+
+  /**
+   * Iterates over all non-zero elements. <p>
+   * NOTE: Implementations may choose to reuse the Element returned for
+   * performance reasons, so if you need a copy of it, you should call {@link #getElement(int)} for the given index
+   *
+   * @return An {@link Iterator} over all non-zero elements
+   */
+  protected abstract Iterator<Element> iterateNonZero();
+  /**
+   * Aggregates a vector by applying a mapping function fm(x) to every component and aggregating
+   * the results with an aggregating function fa(x, y).
+   *
+   * @param aggregator used to combine the current value of the aggregation with the result of map.apply(nextValue)
+   * @param map a function to apply to each element of the vector in turn before passing to the aggregator
+   * @return the result of the aggregation
+   */
+  @Override
+  public double aggregate(DoubleDoubleFunction aggregator, DoubleFunction map) {
+    if (size == 0) {
+      return 0;
+    }
+
+    // If the aggregator is associative and commutative and it's likeLeftMult (fa(0, y) = 0), and there is
+    // at least one zero in the vector (size > getNumNondefaultElements) and applying fm(0) = 0, the result
+    // gets cascaded through the aggregation and the final result will be 0.
+    if (aggregator.isAssociativeAndCommutative() && aggregator.isLikeLeftMult()
+        && size > getNumNondefaultElements() && !map.isDensifying()) {
+      return 0;
+    }
+
+    double result;
+    if (isSequentialAccess() || aggregator.isAssociativeAndCommutative()) {
+      Iterator<Element> iterator;
+      // If fm(0) = 0 and fa(x, 0) = x, we can skip all zero values.
+      if (!map.isDensifying() && aggregator.isLikeRightPlus()) {
+        iterator = iterateNonZero();
+        if (!iterator.hasNext()) {
+          return 0;
+        }
+      } else {
+        iterator = iterator();
+      }
+      Element element = iterator.next();
+      result = map.apply(element.get());
+      while (iterator.hasNext()) {
+        element = iterator.next();
+        result = aggregator.apply(result, map.apply(element.get()));
+      }
+    } else {
+      result = map.apply(getQuick(0));
+      for (int i = 1; i < size; i++) {
+        result = aggregator.apply(result, map.apply(getQuick(i)));
+      }
+    }
+
+    return result;
+  }
+
+  @Override
+  public double aggregate(Vector other, DoubleDoubleFunction aggregator, DoubleDoubleFunction combiner) {
+    Preconditions.checkArgument(size == other.size(), "Vector sizes differ");
+    if (size == 0) {
+      return 0;
+    }
+    return VectorBinaryAggregate.aggregateBest(this, other, aggregator, combiner);
+  }
+
+  /**
+   * Subclasses must override to return an appropriately sparse or dense result
+   *
+   * @param rows    the row cardinality
+   * @param columns the column cardinality
+   * @return a Matrix
+   */
+  protected abstract Matrix matrixLike(int rows, int columns);
+
+  @Override
+  public Vector viewPart(int offset, int length) {
+    if (offset < 0) {
+      throw new IndexException(offset, size);
+    }
+    if (offset + length > size) {
+      throw new IndexException(offset + length, size);
+    }
+    return new VectorView(this, offset, length);
+  }
+
+  @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
+  @Override
+  public Vector clone() {
+    try {
+      AbstractVector r = (AbstractVector) super.clone();
+      r.size = size;
+      r.lengthSquared = lengthSquared;
+      return r;
+    } catch (CloneNotSupportedException e) {
+      throw new IllegalStateException("Can't happen");
+    }
+  }
+
+  @Override
+  public Vector divide(double x) {
+    if (x == 1.0) {
+      return clone();
+    }
+    Vector result = createOptimizedCopy();
+    for (Element element : result.nonZeroes()) {
+      element.set(element.get() / x);
+    }
+    return result;
+  }
+
+  @Override
+  public double dot(Vector x) {
+    if (size != x.size()) {
+      throw new CardinalityException(size, x.size());
+    }
+    if (this == x) {
+      return getLengthSquared();
+    }
+    return aggregate(x, Functions.PLUS, Functions.MULT);
+  }
+
+  protected double dotSelf() {
+    return aggregate(Functions.PLUS, Functions.pow(2));
+  }
+
+  @Override
+  public double get(int index) {
+    if (index < 0 || index >= size) {
+      throw new IndexException(index, size);
+    }
+    return getQuick(index);
+  }
+
+  @Override
+  public Element getElement(int index) {
+    return new LocalElement(index);
+  }
+
+  @Override
+  public Vector normalize() {
+    return divide(Math.sqrt(getLengthSquared()));
+  }
+
+  @Override
+  public Vector normalize(double power) {
+    return divide(norm(power));
+  }
+
+  @Override
+  public Vector logNormalize() {
+    return logNormalize(2.0, Math.sqrt(getLengthSquared()));
+  }
+
+  @Override
+  public Vector logNormalize(double power) {
+    return logNormalize(power, norm(power));
+  }
+
+  public Vector logNormalize(double power, double normLength) {
+    // we can special case certain powers
+    if (Double.isInfinite(power) || power <= 1.0) {
+      throw new IllegalArgumentException("Power must be > 1 and < infinity");
+    } else {
+      double denominator = normLength * Math.log(power);
+      Vector result = createOptimizedCopy();
+      for (Element element : result.nonZeroes()) {
+        element.set(Math.log1p(element.get()) / denominator);
+      }
+      return result;
+    }
+  }
+
+  @Override
+  public double norm(double power) {
+    if (power < 0.0) {
+      throw new IllegalArgumentException("Power must be >= 0");
+    }
+    // We can special case certain powers.
+    if (Double.isInfinite(power)) {
+      return aggregate(Functions.MAX, Functions.ABS);
+    } else if (power == 2.0) {
+      return Math.sqrt(getLengthSquared());
+    } else if (power == 1.0) {
+      double result = 0.0;
+      Iterator<Element> iterator = this.iterateNonZero();
+      while (iterator.hasNext()) {
+        result += Math.abs(iterator.next().get());
+      }
+      return result;
+      // TODO: this should ideally be used, but it's slower.
+      // return aggregate(Functions.PLUS, Functions.ABS);
+    } else if (power == 0.0) {
+      return getNumNonZeroElements();
+    } else {
+      return Math.pow(aggregate(Functions.PLUS, Functions.pow(power)), 1.0 / power);
+    }
+  }
+
+  @Override
+  public double getLengthSquared() {
+    if (lengthSquared >= 0.0) {
+      return lengthSquared;
+    }
+    return lengthSquared = dotSelf();
+  }
+
+  @Override
+  public void invalidateCachedLength() {
+    lengthSquared = -1;
+  }
+
+  @Override
+  public double getDistanceSquared(Vector that) {
+    if (size != that.size()) {
+      throw new CardinalityException(size, that.size());
+    }
+    double thisLength = getLengthSquared();
+    double thatLength = that.getLengthSquared();
+    double dot = dot(that);
+    double distanceEstimate = thisLength + thatLength - 2 * dot;
+    if (distanceEstimate > 1.0e-3 * (thisLength + thatLength)) {
+      // The vectors are far enough from each other that the formula is accurate.
+      return Math.max(distanceEstimate, 0);
+    } else {
+      return aggregate(that, Functions.PLUS, Functions.MINUS_SQUARED);
+    }
+  }
+
+  @Override
+  public double maxValue() {
+    if (size == 0) {
+      return Double.NEGATIVE_INFINITY;
+    }
+    return aggregate(Functions.MAX, Functions.IDENTITY);
+  }
+
+  @Override
+  public int maxValueIndex() {
+    int result = -1;
+    double max = Double.NEGATIVE_INFINITY;
+    int nonZeroElements = 0;
+    Iterator<Element> iter = this.iterateNonZero();
+    while (iter.hasNext()) {
+      nonZeroElements++;
+      Element element = iter.next();
+      double tmp = element.get();
+      if (tmp > max) {
+        max = tmp;
+        result = element.index();
+      }
+    }
+    // if the maxElement is negative and the vector is sparse then any
+    // unfilled element(0.0) could be the maxValue hence we need to
+    // find one of those elements
+    if (nonZeroElements < size && max < 0.0) {
+      for (Element element : all()) {
+        if (element.get() == 0.0) {
+          return element.index();
+        }
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public double minValue() {
+    if (size == 0) {
+      return Double.POSITIVE_INFINITY;
+    }
+    return aggregate(Functions.MIN, Functions.IDENTITY);
+  }
+
+  @Override
+  public int minValueIndex() {
+    int result = -1;
+    double min = Double.POSITIVE_INFINITY;
+    int nonZeroElements = 0;
+    Iterator<Element> iter = this.iterateNonZero();
+    while (iter.hasNext()) {
+      nonZeroElements++;
+      Element element = iter.next();
+      double tmp = element.get();
+      if (tmp < min) {
+        min = tmp;
+        result = element.index();
+      }
+    }
+    // if the maxElement is positive and the vector is sparse then any
+    // unfilled element(0.0) could be the maxValue hence we need to
+    // find one of those elements
+    if (nonZeroElements < size && min > 0.0) {
+      for (Element element : all()) {
+        if (element.get() == 0.0) {
+          return element.index();
+        }
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public Vector plus(double x) {
+    Vector result = createOptimizedCopy();
+    if (x == 0.0) {
+      return result;
+    }
+    return result.assign(Functions.plus(x));
+  }
+
+  @Override
+  public Vector plus(Vector that) {
+    if (size != that.size()) {
+      throw new CardinalityException(size, that.size());
+    }
+    return createOptimizedCopy().assign(that, Functions.PLUS);
+  }
+
+  @Override
+  public Vector minus(Vector that) {
+    if (size != that.size()) {
+      throw new CardinalityException(size, that.size());
+    }
+    return createOptimizedCopy().assign(that, Functions.MINUS);
+  }
+
+  @Override
+  public void set(int index, double value) {
+    if (index < 0 || index >= size) {
+      throw new IndexException(index, size);
+    }
+    setQuick(index, value);
+  }
+
+  @Override
+  public void incrementQuick(int index, double increment) {
+    setQuick(index, getQuick(index) + increment);
+  }
+
+  @Override
+  public Vector times(double x) {
+    if (x == 0.0) {
+      return like();
+    }
+    return createOptimizedCopy().assign(Functions.mult(x));
+  }
+
+  /**
+   * Copy the current vector in the most optimum fashion. Used by immutable methods like plus(), minus().
+   * Use this instead of vector.like().assign(vector). Sub-class can choose to override this method.
+   *
+   * @return a copy of the current vector.
+   */
+  protected Vector createOptimizedCopy() {
+    return createOptimizedCopy(this);
+  }
+
+  private static Vector createOptimizedCopy(Vector vector) {
+    Vector result;
+    if (vector.isDense()) {
+      result = vector.like().assign(vector, Functions.SECOND_LEFT_ZERO);
+    } else {
+      result = vector.clone();
+    }
+    return result;
+  }
+
+  @Override
+  public Vector times(Vector that) {
+    if (size != that.size()) {
+      throw new CardinalityException(size, that.size());
+    }
+
+    if (this.getNumNondefaultElements() <= that.getNumNondefaultElements()) {
+      return createOptimizedCopy(this).assign(that, Functions.MULT);
+    } else {
+      return createOptimizedCopy(that).assign(this, Functions.MULT);
+    }
+  }
+
+  @Override
+  public double zSum() {
+    return aggregate(Functions.PLUS, Functions.IDENTITY);
+  }
+
+  @Override
+  public int getNumNonZeroElements() {
+    int count = 0;
+    Iterator<Element> it = iterateNonZero();
+    while (it.hasNext()) {
+      if (it.next().get() != 0.0) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  @Override
+  public Vector assign(double value) {
+    Iterator<Element> it;
+    if (value == 0.0) {
+      // Make all the non-zero values 0.
+      it = iterateNonZero();
+      while (it.hasNext()) {
+        it.next().set(value);
+      }
+    } else {
+      if (isSequentialAccess() && !isAddConstantTime()) {
+        // Update all the non-zero values and queue the updates for the zero vaues.
+        // The vector will become dense.
+        it = iterator();
+        OrderedIntDoubleMapping updates = new OrderedIntDoubleMapping();
+        while (it.hasNext()) {
+          Element element = it.next();
+          if (element.get() == 0.0) {
+            updates.set(element.index(), value);
+          } else {
+            element.set(value);
+          }
+        }
+        mergeUpdates(updates);
+      } else {
+        for (int i = 0; i < size; ++i) {
+          setQuick(i, value);
+        }
+      }
+    }
+    invalidateCachedLength();
+    return this;
+  }
+
+  @Override
+  public Vector assign(double[] values) {
+    if (size != values.length) {
+      throw new CardinalityException(size, values.length);
+    }
+    if (isSequentialAccess() && !isAddConstantTime()) {
+      OrderedIntDoubleMapping updates = new OrderedIntDoubleMapping();
+      Iterator<Element> it = iterator();
+      while (it.hasNext()) {
+        Element element = it.next();
+        int index = element.index();
+        if (element.get() == 0.0) {
+          updates.set(index, values[index]);
+        } else {
+          element.set(values[index]);
+        }
+      }
+      mergeUpdates(updates);
+    } else {
+      for (int i = 0; i < size; ++i) {
+        setQuick(i, values[i]);
+      }
+    }
+    invalidateCachedLength();
+    return this;
+  }
+
+  @Override
+  public Vector assign(Vector other) {
+    return assign(other, Functions.SECOND);
+  }
+
+  @Override
+  public Vector assign(DoubleDoubleFunction f, double y) {
+    Iterator<Element> iterator = f.apply(0, y) == 0 ? iterateNonZero() : iterator();
+    while (iterator.hasNext()) {
+      Element element = iterator.next();
+      element.set(f.apply(element.get(), y));
+    }
+    invalidateCachedLength();
+    return this;
+  }
+
+  @Override
+  public Vector assign(DoubleFunction f) {
+    Iterator<Element> iterator = !f.isDensifying() ? iterateNonZero() : iterator();
+    while (iterator.hasNext()) {
+      Element element = iterator.next();
+      element.set(f.apply(element.get()));
+    }
+    invalidateCachedLength();
+    return this;
+  }
+
+  @Override
+  public Vector assign(Vector other, DoubleDoubleFunction function) {
+    if (size != other.size()) {
+      throw new CardinalityException(size, other.size());
+    }
+    VectorBinaryAssign.assignBest(this, other, function);
+    invalidateCachedLength();
+    return this;
+  }
+
+  @Override
+  public Matrix cross(Vector other) {
+    Matrix result = matrixLike(size, other.size());
+    Iterator<Vector.Element> it = iterateNonZero();
+    while (it.hasNext()) {
+      Vector.Element e = it.next();
+      int row = e.index();
+      result.assignRow(row, other.times(getQuick(row)));
+    }
+    return result;
+  }
+
+  @Override
+  public final int size() {
+    return size;
+  }
+
+  @Override
+  public String asFormatString() {
+    return toString();
+  }
+
+  @Override
+  public int hashCode() {
+    int result = size;
+    Iterator<Element> iter = iterateNonZero();
+    while (iter.hasNext()) {
+      Element ele = iter.next();
+      result += ele.index() * RandomUtils.hashDouble(ele.get());
+    }
+    return result;
+  }
+
+  /**
+   * Determines whether this {@link Vector} represents the same logical vector as another
+   * object. Two {@link Vector}s are equal (regardless of implementation) if the value at
+   * each index is the same, and the cardinalities are the same.
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof Vector)) {
+      return false;
+    }
+    Vector that = (Vector) o;
+    return size == that.size() && aggregate(that, Functions.PLUS, Functions.MINUS_ABS) == 0.0;
+  }
+
+  @Override
+  public String toString() {
+    return toString(null);
+  }
+
+  public String toString(String[] dictionary) {
+    StringBuilder result = new StringBuilder();
+    result.append('{');
+    for (int index = 0; index < size; index++) {
+      double value = getQuick(index);
+      if (value != 0.0) {
+        result.append(dictionary != null && dictionary.length > index ? dictionary[index] : index);
+        result.append(':');
+        result.append(value);
+        result.append(',');
+      }
+    }
+    if (result.length() > 1) {
+      result.setCharAt(result.length() - 1, '}');
+    } else {
+      result.append('}');
+    }
+    return result.toString();
+  }
+
+  /**
+   * toString() implementation for sparse vectors via {@link #nonZeroes()} method
+   * @return String representation of the vector
+   */
+  public String sparseVectorToString() {
+    Iterator<Element> it = iterateNonZero();
+    if (!it.hasNext()) {
+      return "{}";
+    }
+    else {
+      StringBuilder result = new StringBuilder();
+      result.append('{');
+      while (it.hasNext()) {
+        Vector.Element e = it.next();
+        result.append(e.index());
+        result.append(':');
+        result.append(e.get());
+        result.append(',');
+      }
+      result.setCharAt(result.length() - 1, '}');
+      return result.toString();
+    }
+  }
+
+  protected final class LocalElement implements Element {
+    int index;
+
+    LocalElement(int index) {
+      this.index = index;
+    }
+
+    @Override
+    public double get() {
+      return getQuick(index);
+    }
+
+    @Override
+    public int index() {
+      return index;
+    }
+
+    @Override
+    public void set(double value) {
+      setQuick(index, value);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/mahout/blob/545648f6/core/src/main/java/org/apache/mahout/math/Algebra.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mahout/math/Algebra.java b/core/src/main/java/org/apache/mahout/math/Algebra.java
new file mode 100644
index 0000000..3049057
--- /dev/null
+++ b/core/src/main/java/org/apache/mahout/math/Algebra.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.mahout.math;
+
+public final class Algebra {
+
+  private Algebra() {
+  }
+
+  public static Vector mult(Matrix m, Vector v) {
+    if (m.numRows() != v.size()) {
+      throw new CardinalityException(m.numRows(), v.size());
+    }
+    // Use a Dense Vector for the moment,
+    Vector result = new DenseVector(m.numRows());
+    
+    for (int i = 0; i < m.numRows(); i++) {
+      result.set(i, m.viewRow(i).dot(v));
+    }
+    
+    return result;
+  }
+  
+  /** Returns sqrt(a^2 + b^2) without under/overflow. */
+  public static double hypot(double a, double b) {
+    double r;
+    if (Math.abs(a) > Math.abs(b)) {
+      r = b / a;
+      r = Math.abs(a) * Math.sqrt(1 + r * r);
+    } else if (b != 0) {
+      r = a / b;
+      r = Math.abs(b) * Math.sqrt(1 + r * r);
+    } else {
+      r = 0.0;
+    }
+    return r;
+  }
+  
+  /**
+   * Compute Maximum Absolute Row Sum Norm of input Matrix m
+   * http://mathworld.wolfram.com/MaximumAbsoluteRowSumNorm.html 
+   */
+  public static double getNorm(Matrix m) {
+    double max = 0.0;
+    for (int i = 0; i < m.numRows(); i++) {
+      int sum = 0;
+      Vector cv = m.viewRow(i);
+      for (int j = 0; j < cv.size(); j++) {
+        sum += (int) Math.abs(cv.getQuick(j));
+      }
+      if (sum > max) {
+        max = sum;
+      }
+    }
+    return max;
+  }
+
+}