/* */
/*  Little cms - profiler construction set */
/*  Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
/* */
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
/* */
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
/* OF THIS SOFTWARE. */
/* */
/* This file 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 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. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
/* */
/* As a special exception to the GNU General Public License, if you */
/* distribute this file as part of a program that contains a */
/* configuration script generated by Autoconf, you may include it under */
/* the same distribution terms that you use for the rest of that program. */
/* */
/* Version 1.09a */
/* */
/* Incremental Interpolator */

#include "lcmsprf.h"


/* Res points to a result in XYZ or Lab */

BOOL cdecl cmsxRegressionInterpolatorRGB(LPMEASUREMENT m,
                                       int ColorSpace,
                                       int    RegressionTerms,
                                       BOOL   lUseLocalPatches,
                                       int    MinPatchesToCollect,
                                       double r, double g, double b,
                                       void* Res);


/* -------------------------------------------------------------- Implementation */

/* #define DEBUG 1 */

/* Estimate regression matrix */
static
void EstimateRegression(LPMEASUREMENT m, double r, double g, double b,
                                        int ColorSpace,
                                        LPMATN* ptfm,                                        
                                        int nterms,
                                        BOOL lIncludeAllPatches,
                                        int MinPatchesToCollect)
{
    int nCollected;
    MLRSTATISTICS maxAns;
    int ToCollect;
    SETOFPATCHES collected = cmsxPCollBuildSet(m, false);
    SETOFPATCHES allowed   = cmsxPCollBuildSet(m, true);
    BOOL rc;
    BOOL lPatchesExhausted = false;


    CopyMemory(allowed, m -> Allowed, m->nPatches*sizeof(BOOL));

    *ptfm = NULL;

    ToCollect = max(MinPatchesToCollect, (nterms + 1));

    do {

                if (lIncludeAllPatches) {

                        CopyMemory(collected, allowed, m->nPatches*sizeof(BOOL));
                        lPatchesExhausted = true;
                        ToCollect = nCollected = m->nPatches;
                }
                else
                {

                nCollected =  cmsxPCollPatchesNearRGB(m, m -> Allowed,
                                                            r, g, b,
                                                            ToCollect, collected);

                if (nCollected < ToCollect) {           /* No more patches available */
                        lPatchesExhausted = true;
                }
                else    {
                        ToCollect = nCollected + 1;     /* Start from here in next iteration */
                }
        }

        /* We are going always 3 -> 3 for now.... */
        rc = cmsxRegressionCreateMatrix(m, collected, nterms, ColorSpace, ptfm, &maxAns);


        /* Does fit? */
        if ((rc == false) || maxAns.R2adj < 0.95 ||  maxAns.R2adj > 1.0) {

                maxAns.R2adj = -100;  /* No, repeat               */
        }
	

    } while (!lPatchesExhausted && maxAns.R2adj < 0.95);

#ifdef DEBUG
        printf("R2adj: %g, F: %g\n", maxAns.R2adj, maxAns.F);
#endif

    free(collected);
    free(allowed);
}



BOOL cmsxRegressionInterpolatorRGB(LPMEASUREMENT m,
                                       int ColorSpace,                                       
                                       int    RegressionTerms,
                                       BOOL   lUseLocalPatches,
                                       int    MinPatchesToCollect,
                                       double r, double g, double b,
                                       void* Res)
{
    LPMATN   tfm = NULL;

            
	EstimateRegression(m, r, g, b, ColorSpace, &tfm, RegressionTerms, 
									!lUseLocalPatches, MinPatchesToCollect);

	if (tfm == NULL) return false; 

    switch (ColorSpace) {

        case PT_Lab:

                if (!cmsxRegressionRGB2Lab(r, g, b, tfm, (LPcmsCIELab) Res)) return false;
                break;

        case PT_XYZ:
                if (!cmsxRegressionRGB2XYZ(r, g, b, tfm, (LPcmsCIEXYZ) Res)) return false;
                break;

        default:
                return false;
        }

        MATNfree(tfm);


#ifdef DEBUG
    printf("INTERPOLATED RGB %g,%g,%g Lab %g, %g, %g \n", r , g,  b,
            Lab->L, Lab->a, Lab->b);

#endif
    return true;
}


/* Check the results of a given regression matrix */

static
void CheckOneRegressionMatrix(LPPROFILERCOMMONDATA hdr, LPMATN Matrix, 
										double* Mean, double* Std, double* Max)
{

    cmsCIELab Lab;
    cmsCIEXYZ XYZ;
    double Hit, sum, sum2, n, dE;
    int i;
	cmsCIEXYZ D50;


	D50.X = cmsD50_XYZ() -> X* 100.;
	D50.Y = cmsD50_XYZ() -> Y* 100.;
	D50.Z = cmsD50_XYZ() -> Z* 100.;

    Hit = sum = sum2 = n = 0;
    for (i=0; i < hdr -> m.nPatches; i++) {

        if (hdr -> m.Allowed[i]) {

            LPPATCH p = hdr -> m.Patches + i;

            if (hdr -> PCSType == PT_Lab) {

                    WORD ProfileLabEncoded[3];

                    cmsxRegressionRGB2Lab(p -> Colorant.RGB[0], 
                                        p -> Colorant.RGB[1],
                                        p -> Colorant.RGB[2],
                                        Matrix, &Lab);

                    cmsFloat2LabEncoded(ProfileLabEncoded, &Lab);
                    cmsLabEncoded2Float(&Lab, ProfileLabEncoded);

                    dE = cmsDeltaE(&Lab, &p ->Lab);
            }
            else {
                    cmsCIELab Lab2;

                    cmsxRegressionRGB2XYZ(p -> Colorant.RGB[0], 
                                        p -> Colorant.RGB[1],
                                        p -> Colorant.RGB[2],
                                        Matrix, &XYZ);
					_cmsxClampXYZ100(&XYZ);

                    cmsXYZ2Lab(&D50, &Lab, &XYZ);
                    cmsXYZ2Lab(&D50, &Lab2, &p ->XYZ);

                    dE = cmsDeltaE(&Lab, &Lab2);
            }


            if (dE > Hit)
                Hit = dE;

            sum  += dE;
            sum2 += dE * dE;
            n = n + 1;

        }
    }

    *Mean = sum / n;
    *Std  = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
    *Max  = Hit;

}


/* Trial-and-error in order to get best number of terms. */

int cmsxFindOptimumNumOfTerms(LPPROFILERCOMMONDATA hdr, int nMaxTerms, BOOL* lAllOk)
{
    int i, BestTerms;
    BOOL rc;
    LPMATN Matrix = NULL;
    MLRSTATISTICS Stat;
    double dEmean, dEStd, dEHit, Best;
    BOOL lOneFound;
	

    BestTerms = 4;
    Best = 1000.;
    lOneFound = false;

    for (i=4; i <= nMaxTerms; i++) {		/* 55 */

            rc = cmsxRegressionCreateMatrix(&hdr -> m, hdr -> m.Allowed, 
                                       i, hdr -> PCSType, &Matrix, &Stat);

            if (rc && Stat.R2adj < 1 && Stat.R2adj > 0.6) {
                
                CheckOneRegressionMatrix(hdr, Matrix, &dEmean, &dEStd, &dEHit);

                if (dEStd < Best && dEHit < 50.) {

                        Best = dEStd;
                        BestTerms = i;
                        lOneFound = true;
                }

            }
        MATNfree(Matrix);
        Matrix = NULL;
    }

    *lAllOk = lOneFound;
	
    return BestTerms;
}


