// Created on: 1993-03-09
// Created by: JCV
// Copyright (c) 1993-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

// 14-Mar-96 : xab  portage hp
// pmn : 28-Jun-96 Distinction entre la continuite en U et V (bug PRO4625)
// pmn : 07-Jan-97 Centralisation des verif rational (PRO6834)
//       et ajout des InvalideCache() dans les SetWeight*(PRO6833)
// RBD : 15-10-98 ; Le cache est maintenant calcule sur [-1,1] (pro15537).
// jct : 19-01-99 ; permutation de urational et vrational dans Rational.
#define No_Standard_OutOfRange

#include <BSplCLib.hxx>
#include <BSplSLib.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_Geometry.hxx>
#include <Geom_UndefinedDerivative.hxx>
#include <gp.hxx>
#include <gp_Pnt.hxx>
#include <gp_Trsf.hxx>
#include <Precision.hxx>
#include <Standard_ConstructionError.hxx>
#include <Standard_DomainError.hxx>
#include <Standard_NotImplemented.hxx>
#include <Standard_OutOfRange.hxx>
#include <Standard_Type.hxx>

IMPLEMENT_STANDARD_RTTIEXT(Geom_BSplineSurface, Geom_BoundedSurface)

//=======================================================================
// function : CheckSurfaceData
// purpose  : Internal use only.
//=======================================================================
static void CheckSurfaceData(const TColgp_Array2OfPnt&      SPoles,
                             const TColStd_Array1OfReal&    SUKnots,
                             const TColStd_Array1OfReal&    SVKnots,
                             const TColStd_Array1OfInteger& SUMults,
                             const TColStd_Array1OfInteger& SVMults,
                             const Standard_Integer         UDegree,
                             const Standard_Integer         VDegree,
                             const Standard_Boolean         UPeriodic,
                             const Standard_Boolean         VPeriodic)
{
  if (UDegree < 1 || UDegree > Geom_BSplineSurface::MaxDegree() || VDegree < 1
      || VDegree > Geom_BSplineSurface::MaxDegree())
  {
    throw Standard_ConstructionError("Geom_BSplineSurface: invalid degree");
  }
  if (SPoles.ColLength() < 2 || SPoles.RowLength() < 2)
  {
    throw Standard_ConstructionError("Geom_BSplineSurface: at least 2 poles required");
  }

  if (SUKnots.Length() != SUMults.Length() || SVKnots.Length() != SVMults.Length())
  {
    throw Standard_ConstructionError("Geom_BSplineSurface: Knot and Mult array size mismatch");
  }

  Standard_Integer i;
  for (i = SUKnots.Lower(); i < SUKnots.Upper(); i++)
  {
    if (SUKnots(i + 1) - SUKnots(i) <= Epsilon(Abs(SUKnots(i))))
    {
      throw Standard_ConstructionError("Geom_BSplineSurface: UKnots interval values too close");
    }
  }

  for (i = SVKnots.Lower(); i < SVKnots.Upper(); i++)
  {
    if (SVKnots(i + 1) - SVKnots(i) <= Epsilon(Abs(SVKnots(i))))
    {
      throw Standard_ConstructionError("Geom_BSplineSurface: VKnots interval values too close");
    }
  }

  if (SPoles.ColLength() != BSplCLib::NbPoles(UDegree, UPeriodic, SUMults))
    throw Standard_ConstructionError("Geom_BSplineSurface: # U Poles and degree mismatch");

  if (SPoles.RowLength() != BSplCLib::NbPoles(VDegree, VPeriodic, SVMults))
    throw Standard_ConstructionError("Geom_BSplineSurface: # V Poles and degree mismatch");
}

//=======================================================================
// function : Rational
// purpose  : Internal use only.
//=======================================================================

static void Rational(const TColStd_Array2OfReal& Weights,
                     Standard_Boolean&           Urational,
                     Standard_Boolean&           Vrational)
{
  Standard_Integer I, J;
  J         = Weights.LowerCol();
  Vrational = Standard_False;
  while (!Vrational && J <= Weights.UpperCol())
  {
    I = Weights.LowerRow();
    while (!Vrational && I <= Weights.UpperRow() - 1)
    {
      Vrational = (Abs(Weights(I, J) - Weights(I + 1, J)) > Epsilon(Abs(Weights(I, J))));
      I++;
    }
    J++;
  }

  I         = Weights.LowerRow();
  Urational = Standard_False;
  while (!Urational && I <= Weights.UpperRow())
  {
    J = Weights.LowerCol();
    while (!Urational && J <= Weights.UpperCol() - 1)
    {
      Urational = (Abs(Weights(I, J) - Weights(I, J + 1)) > Epsilon(Abs(Weights(I, J))));
      J++;
    }
    I++;
  }
}

//=================================================================================================

Handle(Geom_Geometry) Geom_BSplineSurface::Copy() const
{
  Handle(Geom_BSplineSurface) S;
  if (urational || vrational)
    S = new Geom_BSplineSurface(poles->Array2(),
                                weights->Array2(),
                                uknots->Array1(),
                                vknots->Array1(),
                                umults->Array1(),
                                vmults->Array1(),
                                udeg,
                                vdeg,
                                uperiodic,
                                vperiodic);
  else
    S = new Geom_BSplineSurface(poles->Array2(),
                                uknots->Array1(),
                                vknots->Array1(),
                                umults->Array1(),
                                vmults->Array1(),
                                udeg,
                                vdeg,
                                uperiodic,
                                vperiodic);
  return S;
}

//=================================================================================================

Geom_BSplineSurface::Geom_BSplineSurface(const TColgp_Array2OfPnt&      Poles,
                                         const TColStd_Array1OfReal&    UKnots,
                                         const TColStd_Array1OfReal&    VKnots,
                                         const TColStd_Array1OfInteger& UMults,
                                         const TColStd_Array1OfInteger& VMults,
                                         const Standard_Integer         UDegree,
                                         const Standard_Integer         VDegree,
                                         const Standard_Boolean         UPeriodic,
                                         const Standard_Boolean         VPeriodic)
    : urational(Standard_False),
      vrational(Standard_False),
      uperiodic(UPeriodic),
      vperiodic(VPeriodic),
      udeg(UDegree),
      vdeg(VDegree),
      maxderivinvok(0)

{

  // check

  CheckSurfaceData(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree, UPeriodic, VPeriodic);

  // copy arrays

  poles                 = new TColgp_HArray2OfPnt(1, Poles.ColLength(), 1, Poles.RowLength());
  poles->ChangeArray2() = Poles;

  weights = new TColStd_HArray2OfReal(1, Poles.ColLength(), 1, Poles.RowLength(), 1.0);

  uknots                 = new TColStd_HArray1OfReal(1, UKnots.Length());
  uknots->ChangeArray1() = UKnots;

  umults                 = new TColStd_HArray1OfInteger(1, UMults.Length());
  umults->ChangeArray1() = UMults;

  vknots                 = new TColStd_HArray1OfReal(1, VKnots.Length());
  vknots->ChangeArray1() = VKnots;

  vmults                 = new TColStd_HArray1OfInteger(1, VMults.Length());
  vmults->ChangeArray1() = VMults;

  UpdateUKnots();
  UpdateVKnots();
}

//=================================================================================================

Geom_BSplineSurface::Geom_BSplineSurface(const TColgp_Array2OfPnt&      Poles,
                                         const TColStd_Array2OfReal&    Weights,
                                         const TColStd_Array1OfReal&    UKnots,
                                         const TColStd_Array1OfReal&    VKnots,
                                         const TColStd_Array1OfInteger& UMults,
                                         const TColStd_Array1OfInteger& VMults,
                                         const Standard_Integer         UDegree,
                                         const Standard_Integer         VDegree,
                                         const Standard_Boolean         UPeriodic,
                                         const Standard_Boolean         VPeriodic)
    : urational(Standard_False),
      vrational(Standard_False),
      uperiodic(UPeriodic),
      vperiodic(VPeriodic),
      udeg(UDegree),
      vdeg(VDegree),
      maxderivinvok(0)
{
  // check weights

  if (Weights.ColLength() != Poles.ColLength())
    throw Standard_ConstructionError(
      "Geom_BSplineSurface: U Weights and Poles array size mismatch");

  if (Weights.RowLength() != Poles.RowLength())
    throw Standard_ConstructionError(
      "Geom_BSplineSurface: V Weights and Poles array size mismatch");

  Standard_Integer i, j;
  for (i = Weights.LowerRow(); i <= Weights.UpperRow(); i++)
  {
    for (j = Weights.LowerCol(); j <= Weights.UpperCol(); j++)
    {
      if (Weights(i, j) <= gp::Resolution())
        throw Standard_ConstructionError("Geom_BSplineSurface: Weights values too small");
    }
  }

  // check really rational

  Rational(Weights, urational, vrational);

  // check

  CheckSurfaceData(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree, UPeriodic, VPeriodic);

  // copy arrays

  poles                 = new TColgp_HArray2OfPnt(1, Poles.ColLength(), 1, Poles.RowLength());
  poles->ChangeArray2() = Poles;

  weights                 = new TColStd_HArray2OfReal(1, Poles.ColLength(), 1, Poles.RowLength());
  weights->ChangeArray2() = Weights;

  uknots                 = new TColStd_HArray1OfReal(1, UKnots.Length());
  uknots->ChangeArray1() = UKnots;

  umults                 = new TColStd_HArray1OfInteger(1, UMults.Length());
  umults->ChangeArray1() = UMults;

  vknots                 = new TColStd_HArray1OfReal(1, VKnots.Length());
  vknots->ChangeArray1() = VKnots;

  vmults                 = new TColStd_HArray1OfInteger(1, VMults.Length());
  vmults->ChangeArray1() = VMults;

  UpdateUKnots();
  UpdateVKnots();
}

//=================================================================================================

void Geom_BSplineSurface::ExchangeUV()
{
  Standard_Integer LC = poles->LowerCol();
  Standard_Integer UC = poles->UpperCol();
  Standard_Integer LR = poles->LowerRow();
  Standard_Integer UR = poles->UpperRow();

  Handle(TColgp_HArray2OfPnt)   npoles = new TColgp_HArray2OfPnt(LC, UC, LR, UR);
  Handle(TColStd_HArray2OfReal) nweights;
  if (!weights.IsNull())
  {
    nweights = new TColStd_HArray2OfReal(LC, UC, LR, UR);
  }

  const TColgp_Array2OfPnt&   spoles   = poles->Array2();
  const TColStd_Array2OfReal* sweights = !weights.IsNull() ? &weights->Array2() : NULL;

  TColgp_Array2OfPnt&   snpoles   = npoles->ChangeArray2();
  TColStd_Array2OfReal* snweights = !nweights.IsNull() ? &nweights->ChangeArray2() : NULL;
  for (Standard_Integer i = LC; i <= UC; i++)
  {
    for (Standard_Integer j = LR; j <= UR; j++)
    {
      snpoles(i, j) = spoles(j, i);
      if (snweights != NULL)
      {
        snweights->ChangeValue(i, j) = sweights->Value(j, i);
      }
    }
  }
  poles   = npoles;
  weights = nweights;

  std::swap(urational, vrational);
  std::swap(uperiodic, vperiodic);
  std::swap(udeg, vdeg);
  std::swap(uknots, vknots);
  std::swap(umults, vmults);

  UpdateUKnots();
  UpdateVKnots();
}

//=================================================================================================

void Geom_BSplineSurface::IncreaseDegree(const Standard_Integer UDegree,
                                         const Standard_Integer VDegree)
{
  if (UDegree != udeg)
  {
    if (UDegree < udeg || UDegree > Geom_BSplineSurface::MaxDegree())
      throw Standard_ConstructionError("Geom_BSplineSurface::IncreaseDegree: bad U degree value");

    Standard_Integer FromK1 = FirstUKnotIndex();
    Standard_Integer ToK2   = LastUKnotIndex();

    Standard_Integer Step = UDegree - udeg;

    Handle(TColgp_HArray2OfPnt) npoles =
      new TColgp_HArray2OfPnt(1,
                              poles->ColLength() + Step * (ToK2 - FromK1),
                              1,
                              poles->RowLength());

    Standard_Integer nbknots =
      BSplCLib::IncreaseDegreeCountKnots(udeg, UDegree, uperiodic, umults->Array1());

    Handle(TColStd_HArray1OfReal) nknots = new TColStd_HArray1OfReal(1, nbknots);

    Handle(TColStd_HArray1OfInteger) nmults = new TColStd_HArray1OfInteger(1, nbknots);

    Handle(TColStd_HArray2OfReal) nweights =
      new TColStd_HArray2OfReal(1, npoles->ColLength(), 1, npoles->RowLength(), 1.);

    if (urational || vrational)
    {

      BSplSLib::IncreaseDegree(Standard_True,
                               udeg,
                               UDegree,
                               uperiodic,
                               poles->Array2(),
                               &weights->Array2(),
                               uknots->Array1(),
                               umults->Array1(),
                               npoles->ChangeArray2(),
                               &nweights->ChangeArray2(),
                               nknots->ChangeArray1(),
                               nmults->ChangeArray1());
    }
    else
    {

      BSplSLib::IncreaseDegree(Standard_True,
                               udeg,
                               UDegree,
                               uperiodic,
                               poles->Array2(),
                               BSplSLib::NoWeights(),
                               uknots->Array1(),
                               umults->Array1(),
                               npoles->ChangeArray2(),
                               BSplSLib::NoWeights(),
                               nknots->ChangeArray1(),
                               nmults->ChangeArray1());
    }
    udeg    = UDegree;
    poles   = npoles;
    weights = nweights;
    uknots  = nknots;
    umults  = nmults;
    UpdateUKnots();
  }

  if (VDegree != vdeg)
  {
    if (VDegree < vdeg || VDegree > Geom_BSplineSurface::MaxDegree())
      throw Standard_ConstructionError("Geom_BSplineSurface::IncreaseDegree: bad V degree value");

    Standard_Integer FromK1 = FirstVKnotIndex();
    Standard_Integer ToK2   = LastVKnotIndex();

    Standard_Integer Step = VDegree - vdeg;

    Handle(TColgp_HArray2OfPnt) npoles =
      new TColgp_HArray2OfPnt(1,
                              poles->ColLength(),
                              1,
                              poles->RowLength() + Step * (ToK2 - FromK1));

    Standard_Integer nbknots =
      BSplCLib::IncreaseDegreeCountKnots(vdeg, VDegree, vperiodic, vmults->Array1());

    Handle(TColStd_HArray1OfReal) nknots = new TColStd_HArray1OfReal(1, nbknots);

    Handle(TColStd_HArray1OfInteger) nmults = new TColStd_HArray1OfInteger(1, nbknots);

    Handle(TColStd_HArray2OfReal) nweights =
      new TColStd_HArray2OfReal(1, npoles->ColLength(), 1, npoles->RowLength(), 1.);

    if (urational || vrational)
    {

      BSplSLib::IncreaseDegree(Standard_False,
                               vdeg,
                               VDegree,
                               vperiodic,
                               poles->Array2(),
                               &weights->Array2(),
                               vknots->Array1(),
                               vmults->Array1(),
                               npoles->ChangeArray2(),
                               &nweights->ChangeArray2(),
                               nknots->ChangeArray1(),
                               nmults->ChangeArray1());
    }
    else
    {

      BSplSLib::IncreaseDegree(Standard_False,
                               vdeg,
                               VDegree,
                               vperiodic,
                               poles->Array2(),
                               BSplSLib::NoWeights(),
                               vknots->Array1(),
                               vmults->Array1(),
                               npoles->ChangeArray2(),
                               BSplSLib::NoWeights(),
                               nknots->ChangeArray1(),
                               nmults->ChangeArray1());
    }
    vdeg    = VDegree;
    poles   = npoles;
    weights = nweights;
    vknots  = nknots;
    vmults  = nmults;
    UpdateVKnots();
  }
}

//=================================================================================================

void Geom_BSplineSurface::IncreaseUMultiplicity(const Standard_Integer UIndex,
                                                const Standard_Integer M)
{
  TColStd_Array1OfReal k(1, 1);
  k(1) = uknots->Value(UIndex);
  TColStd_Array1OfInteger m(1, 1);
  m(1) = M - umults->Value(UIndex);
  InsertUKnots(k, m, Epsilon(1.), Standard_True);
}

//=================================================================================================

void Geom_BSplineSurface::IncreaseUMultiplicity(const Standard_Integer FromI1,
                                                const Standard_Integer ToI2,
                                                const Standard_Integer M)
{
  Handle(TColStd_HArray1OfReal) tk = uknots;
  TColStd_Array1OfReal          k((uknots->Array1())(FromI1), FromI1, ToI2);
  TColStd_Array1OfInteger       m(FromI1, ToI2);
  for (Standard_Integer i = FromI1; i <= ToI2; i++)
    m(i) = M - umults->Value(i);
  InsertUKnots(k, m, Epsilon(1.), Standard_True);
}

//=================================================================================================

void Geom_BSplineSurface::IncreaseVMultiplicity(const Standard_Integer VIndex,
                                                const Standard_Integer M)
{
  TColStd_Array1OfReal k(1, 1);
  k(1) = vknots->Value(VIndex);
  TColStd_Array1OfInteger m(1, 1);
  m(1) = M - vmults->Value(VIndex);
  InsertVKnots(k, m, Epsilon(1.), Standard_True);
}

//=================================================================================================

void Geom_BSplineSurface::IncreaseVMultiplicity(const Standard_Integer FromI1,
                                                const Standard_Integer ToI2,
                                                const Standard_Integer M)
{
  Handle(TColStd_HArray1OfReal) tk = vknots;
  TColStd_Array1OfReal          k((vknots->Array1())(FromI1), FromI1, ToI2);
  TColStd_Array1OfInteger       m(FromI1, ToI2);
  for (Standard_Integer i = FromI1; i <= ToI2; i++)
    m(i) = M - vmults->Value(i);
  InsertVKnots(k, m, Epsilon(1.), Standard_True);
}

//=================================================================================================

void Geom_BSplineSurface::segment(const Standard_Real    U1,
                                  const Standard_Real    U2,
                                  const Standard_Real    V1,
                                  const Standard_Real    V2,
                                  const Standard_Real    EpsU,
                                  const Standard_Real    EpsV,
                                  const Standard_Boolean SegmentInU,
                                  const Standard_Boolean SegmentInV)
{
  Standard_Real deltaU = U2 - U1;
  if (uperiodic)
  {
    Standard_Real aUPeriod = uknots->Last() - uknots->First();
    if (deltaU - aUPeriod > Precision::PConfusion())
      throw Standard_DomainError("Geom_BSplineSurface::Segment");
    if (deltaU > aUPeriod)
      deltaU = aUPeriod;
  }

  Standard_Real deltaV = V2 - V1;
  if (vperiodic)
  {
    Standard_Real aVPeriod = vknots->Last() - vknots->First();
    if (deltaV - aVPeriod > Precision::PConfusion())
      throw Standard_DomainError("Geom_BSplineSurface::Segment");
    if (deltaV > aVPeriod)
      deltaV = aVPeriod;
  }

  Standard_Real    NewU1, NewU2, NewV1, NewV2;
  Standard_Real    U, V;
  Standard_Integer indexU, indexV;

  indexU = 0;
  BSplCLib::LocateParameter(udeg,
                            uknots->Array1(),
                            umults->Array1(),
                            U1,
                            uperiodic,
                            uknots->Lower(),
                            uknots->Upper(),
                            indexU,
                            NewU1);
  indexU = 0;
  BSplCLib::LocateParameter(udeg,
                            uknots->Array1(),
                            umults->Array1(),
                            U2,
                            uperiodic,
                            uknots->Lower(),
                            uknots->Upper(),
                            indexU,
                            NewU2);
  if (SegmentInU)
  {
    // inserting the UKnots
    TColStd_Array1OfReal    UKnots(1, 2);
    TColStd_Array1OfInteger UMults(1, 2);
    UKnots(1) = Min(NewU1, NewU2);
    UKnots(2) = Max(NewU1, NewU2);
    UMults(1) = UMults(2) = udeg;

    InsertUKnots(UKnots, UMults, EpsU);
  }

  indexV = 0;
  BSplCLib::LocateParameter(vdeg,
                            vknots->Array1(),
                            vmults->Array1(),
                            V1,
                            vperiodic,
                            vknots->Lower(),
                            vknots->Upper(),
                            indexV,
                            NewV1);
  indexV = 0;
  BSplCLib::LocateParameter(vdeg,
                            vknots->Array1(),
                            vmults->Array1(),
                            V2,
                            vperiodic,
                            vknots->Lower(),
                            vknots->Upper(),
                            indexV,
                            NewV2);
  if (SegmentInV)
  {
    // Inserting the VKnots
    TColStd_Array1OfReal    VKnots(1, 2);
    TColStd_Array1OfInteger VMults(1, 2);

    VKnots(1) = Min(NewV1, NewV2);
    VKnots(2) = Max(NewV1, NewV2);
    VMults(1) = VMults(2) = vdeg;
    InsertVKnots(VKnots, VMults, EpsV);
  }

  if (uperiodic && SegmentInU)
  { // set the origine at NewU1
    Standard_Integer index = 0;
    BSplCLib::LocateParameter(udeg,
                              uknots->Array1(),
                              umults->Array1(),
                              U1,
                              uperiodic,
                              uknots->Lower(),
                              uknots->Upper(),
                              index,
                              U);
    if (Abs(uknots->Value(index + 1) - U) <= EpsU)
      index++;
    SetUOrigin(index);
    SetUNotPeriodic();
  }

  // compute index1 and index2 to set the new knots and mults
  Standard_Integer index1U = 0, index2U = 0;
  Standard_Integer FromU1 = uknots->Lower();
  Standard_Integer ToU2   = uknots->Upper();
  BSplCLib::LocateParameter(udeg,
                            uknots->Array1(),
                            umults->Array1(),
                            NewU1,
                            uperiodic,
                            FromU1,
                            ToU2,
                            index1U,
                            U);
  if (Abs(uknots->Value(index1U + 1) - U) <= EpsU)
    index1U++;
  BSplCLib::LocateParameter(udeg,
                            uknots->Array1(),
                            umults->Array1(),
                            NewU1 + deltaU,
                            uperiodic,
                            FromU1,
                            ToU2,
                            index2U,
                            U);
  if (Abs(uknots->Value(index2U + 1) - U) <= EpsU || index2U == index1U)
    index2U++;

  Standard_Integer nbuknots = index2U - index1U + 1;

  Handle(TColStd_HArray1OfReal)    nuknots = new TColStd_HArray1OfReal(1, nbuknots);
  Handle(TColStd_HArray1OfInteger) numults = new TColStd_HArray1OfInteger(1, nbuknots);

  Standard_Integer i, k = 1;
  for (i = index1U; i <= index2U; i++)
  {
    nuknots->SetValue(k, uknots->Value(i));
    numults->SetValue(k, umults->Value(i));
    k++;
  }
  if (SegmentInU)
  {
    numults->SetValue(1, udeg + 1);
    numults->SetValue(nbuknots, udeg + 1);
  }

  if (vperiodic && SegmentInV)
  { // set the origine at NewV1
    Standard_Integer index = 0;
    BSplCLib::LocateParameter(vdeg,
                              vknots->Array1(),
                              vmults->Array1(),
                              V1,
                              vperiodic,
                              vknots->Lower(),
                              vknots->Upper(),
                              index,
                              V);
    if (Abs(vknots->Value(index + 1) - V) <= EpsV)
      index++;
    SetVOrigin(index);
    SetVNotPeriodic();
  }

  // compute index1 and index2 to set the new knots and mults
  Standard_Integer index1V = 0, index2V = 0;
  Standard_Integer FromV1 = vknots->Lower();
  Standard_Integer ToV2   = vknots->Upper();
  BSplCLib::LocateParameter(vdeg,
                            vknots->Array1(),
                            vmults->Array1(),
                            NewV1,
                            vperiodic,
                            FromV1,
                            ToV2,
                            index1V,
                            V);
  if (Abs(vknots->Value(index1V + 1) - V) <= EpsV)
    index1V++;
  BSplCLib::LocateParameter(vdeg,
                            vknots->Array1(),
                            vmults->Array1(),
                            NewV1 + deltaV,
                            vperiodic,
                            FromV1,
                            ToV2,
                            index2V,
                            V);
  if (Abs(vknots->Value(index2V + 1) - V) <= EpsV || index2V == index1V)
    index2V++;

  Standard_Integer nbvknots = index2V - index1V + 1;

  Handle(TColStd_HArray1OfReal)    nvknots = new TColStd_HArray1OfReal(1, nbvknots);
  Handle(TColStd_HArray1OfInteger) nvmults = new TColStd_HArray1OfInteger(1, nbvknots);

  k = 1;
  for (i = index1V; i <= index2V; i++)
  {
    nvknots->SetValue(k, vknots->Value(i));
    nvmults->SetValue(k, vmults->Value(i));
    k++;
  }
  if (SegmentInV)
  {
    nvmults->SetValue(1, vdeg + 1);
    nvmults->SetValue(nbvknots, vdeg + 1);
  }

  // compute index1 and index2 to set the new poles and weights
  Standard_Integer pindex1U = BSplCLib::PoleIndex(udeg, index1U, uperiodic, umults->Array1());
  Standard_Integer pindex2U = BSplCLib::PoleIndex(udeg, index2U, uperiodic, umults->Array1());

  pindex1U++;
  pindex2U = Min(pindex2U + 1, poles->ColLength());

  Standard_Integer nbupoles = pindex2U - pindex1U + 1;

  // compute index1 and index2 to set the new poles and weights
  Standard_Integer pindex1V = BSplCLib::PoleIndex(vdeg, index1V, vperiodic, vmults->Array1());
  Standard_Integer pindex2V = BSplCLib::PoleIndex(vdeg, index2V, vperiodic, vmults->Array1());

  pindex1V++;
  pindex2V = Min(pindex2V + 1, poles->RowLength());

  Standard_Integer nbvpoles = pindex2V - pindex1V + 1;

  Handle(TColStd_HArray2OfReal) nweights;

  Handle(TColgp_HArray2OfPnt) npoles = new TColgp_HArray2OfPnt(1, nbupoles, 1, nbvpoles);
  k                                  = 1;
  Standard_Integer j, l;
  if (urational || vrational)
  {
    nweights = new TColStd_HArray2OfReal(1, nbupoles, 1, nbvpoles);
    for (i = pindex1U; i <= pindex2U; i++)
    {
      l = 1;
      for (j = pindex1V; j <= pindex2V; j++)
      {
        npoles->SetValue(k, l, poles->Value(i, j));
        nweights->SetValue(k, l, weights->Value(i, j));
        l++;
      }
      k++;
    }
  }
  else
  {
    for (i = pindex1U; i <= pindex2U; i++)
    {
      l = 1;
      for (j = pindex1V; j <= pindex2V; j++)
      {
        npoles->SetValue(k, l, poles->Value(i, j));
        l++;
      }
      k++;
    }
  }

  uknots = nuknots;
  umults = numults;
  vknots = nvknots;
  vmults = nvmults;
  poles  = npoles;
  if (urational || vrational)
    weights = nweights;
  else
    weights = new TColStd_HArray2OfReal(1, poles->ColLength(), 1, poles->RowLength(), 1.0);

  maxderivinvok = 0;
  UpdateUKnots();
  UpdateVKnots();
}

//=================================================================================================

void Geom_BSplineSurface::Segment(const Standard_Real U1,
                                  const Standard_Real U2,
                                  const Standard_Real V1,
                                  const Standard_Real V2,
                                  const Standard_Real theUTolerance,
                                  const Standard_Real theVTolerance)
{
  if ((U2 < U1) || (V2 < V1))
    throw Standard_DomainError("Geom_BSplineSurface::Segment");

  Standard_Real aMaxU = Max(Abs(U2), Abs(U1));
  Standard_Real EpsU  = Max(Epsilon(aMaxU), theUTolerance);

  Standard_Real aMaxV = Max(Abs(V2), Abs(V1));
  Standard_Real EpsV  = Max(Epsilon(aMaxV), theVTolerance);

  segment(U1, U2, V1, V2, EpsU, EpsV, Standard_True, Standard_True);
}

//=================================================================================================

void Geom_BSplineSurface::CheckAndSegment(const Standard_Real U1,
                                          const Standard_Real U2,
                                          const Standard_Real V1,
                                          const Standard_Real V2,
                                          const Standard_Real theUTolerance,
                                          const Standard_Real theVTolerance)
{

  if ((U2 < U1) || (V2 < V1))
    throw Standard_DomainError("Geom_BSplineSurface::CheckAndSegment");

  Standard_Real aMaxU = Max(Abs(U2), Abs(U1));
  Standard_Real EpsU  = Max(Epsilon(aMaxU), theUTolerance);

  Standard_Real aMaxV = Max(Abs(V2), Abs(V1));
  Standard_Real EpsV  = Max(Epsilon(aMaxV), theVTolerance);

  Standard_Boolean segment_in_U = Standard_True;
  Standard_Boolean segment_in_V = Standard_True;
  segment_in_U                  = (Abs(U1 - uknots->Value(uknots->Lower())) > EpsU)
                 || (Abs(U2 - uknots->Value(uknots->Upper())) > EpsU);
  segment_in_V = (Abs(V1 - vknots->Value(vknots->Lower())) > EpsV)
                 || (Abs(V2 - vknots->Value(vknots->Upper())) > EpsV);

  segment(U1, U2, V1, V2, EpsU, EpsV, segment_in_U, segment_in_V);
}

//=================================================================================================

void Geom_BSplineSurface::SetUKnot(const Standard_Integer UIndex, const Standard_Real K)
{
  if (UIndex < 1 || UIndex > uknots->Length())
    throw Standard_OutOfRange("Geom_BSplineSurface::SetUKnot: Index and #knots mismatch");

  Standard_Integer NewIndex = UIndex;
  Standard_Real    DU       = Abs(Epsilon(K));
  if (UIndex == 1)
  {
    if (K >= uknots->Value(2) - DU)
      throw Standard_ConstructionError("Geom_BSplineSurface::SetUKnot: K out of range");
  }
  else if (UIndex == uknots->Length())
  {
    if (K <= uknots->Value(uknots->Length() - 1) + DU)
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetUKnot: K out of range");
    }
  }
  else
  {
    if (K <= uknots->Value(NewIndex - 1) + DU || K >= uknots->Value(NewIndex + 1) - DU)
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetUKnot: K out of range");
    }
  }

  if (K != uknots->Value(NewIndex))
  {
    uknots->SetValue(NewIndex, K);
    maxderivinvok = 0;
    UpdateUKnots();
  }
}

//=================================================================================================

void Geom_BSplineSurface::SetUKnots(const TColStd_Array1OfReal& UK)
{

  Standard_Integer Lower = UK.Lower();
  Standard_Integer Upper = UK.Upper();
  if (Lower < 1 || Lower > uknots->Length() || Upper < 1 || Upper > uknots->Length())
  {
    throw Standard_OutOfRange("Geom_BSplineSurface::SetUKnots: invalid array dimension");
  }
  if (Lower > 1)
  {
    if (Abs(UK(Lower) - uknots->Value(Lower - 1)) <= gp::Resolution())
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetUKnots: invalid knot value");
    }
  }
  if (Upper < uknots->Length())
  {
    if (Abs(UK(Upper) - uknots->Value(Upper + 1)) <= gp::Resolution())
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetUKnots: invalid knot value");
    }
  }
  Standard_Real K1 = UK(Lower);
  for (Standard_Integer i = Lower; i <= Upper; i++)
  {
    uknots->SetValue(i, UK(i));
    if (i != Lower)
    {
      if (Abs(UK(i) - K1) <= gp::Resolution())
      {
        throw Standard_ConstructionError("Geom_BSplineSurface::SetUKnots: invalid knot value");
      }
      K1 = UK(i);
    }
  }

  maxderivinvok = 0;
  UpdateUKnots();
}

//=================================================================================================

void Geom_BSplineSurface::SetUKnot(const Standard_Integer UIndex,
                                   const Standard_Real    K,
                                   const Standard_Integer M)
{
  IncreaseUMultiplicity(UIndex, M);
  SetUKnot(UIndex, K);
}

//=================================================================================================

void Geom_BSplineSurface::SetVKnot(const Standard_Integer VIndex, const Standard_Real K)
{
  if (VIndex < 1 || VIndex > vknots->Length())
    throw Standard_OutOfRange("Geom_BSplineSurface::SetVKnot: Index and #knots mismatch");
  Standard_Integer NewIndex = VIndex + vknots->Lower() - 1;
  Standard_Real    DV       = Abs(Epsilon(K));
  if (VIndex == 1)
  {
    if (K >= vknots->Value(2) - DV)
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetVKnot: K out of range");
    }
  }
  else if (VIndex == vknots->Length())
  {
    if (K <= vknots->Value(vknots->Length() - 1) + DV)
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetVKnot: K out of range");
    }
  }
  else
  {
    if (K <= vknots->Value(NewIndex - 1) + DV || K >= vknots->Value(NewIndex + 1) - DV)
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetVKnot: K out of range");
    }
  }

  if (K != vknots->Value(NewIndex))
  {
    vknots->SetValue(NewIndex, K);
    maxderivinvok = 0;
    UpdateVKnots();
  }
}

//=================================================================================================

void Geom_BSplineSurface::SetVKnots(const TColStd_Array1OfReal& VK)
{

  Standard_Integer Lower = VK.Lower();
  Standard_Integer Upper = VK.Upper();
  if (Lower < 1 || Lower > vknots->Length() || Upper < 1 || Upper > vknots->Length())
  {
    throw Standard_OutOfRange("Geom_BSplineSurface::SetVKnots: invalid array dimension");
  }
  if (Lower > 1)
  {
    if (Abs(VK(Lower) - vknots->Value(Lower - 1)) <= gp::Resolution())
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetVKnots: invalid knot value");
    }
  }
  if (Upper < vknots->Length())
  {
    if (Abs(VK(Upper) - vknots->Value(Upper + 1)) <= gp::Resolution())
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetVKnots: invalid knot value");
    }
  }
  Standard_Real K1 = VK(Lower);
  for (Standard_Integer i = Lower; i <= Upper; i++)
  {
    vknots->SetValue(i, VK(i));
    if (i != Lower)
    {
      if (Abs(VK(i) - K1) <= gp::Resolution())
      {
        throw Standard_ConstructionError("Geom_BSplineSurface::SetVKnots: invalid knot value");
      }
      K1 = VK(i);
    }
  }

  maxderivinvok = 0;
  UpdateVKnots();
}

//=================================================================================================

void Geom_BSplineSurface::SetVKnot(const Standard_Integer VIndex,
                                   const Standard_Real    K,
                                   const Standard_Integer M)
{
  IncreaseVMultiplicity(VIndex, M);
  SetVKnot(VIndex, K);
}

//=================================================================================================

void Geom_BSplineSurface::InsertUKnot(const Standard_Real    U,
                                      const Standard_Integer M,
                                      const Standard_Real    ParametricTolerance,
                                      const Standard_Boolean Add)
{
  TColStd_Array1OfReal k(1, 1);
  k(1) = U;
  TColStd_Array1OfInteger m(1, 1);
  m(1) = M;
  InsertUKnots(k, m, ParametricTolerance, Add);
}

//=================================================================================================

void Geom_BSplineSurface::InsertVKnot(const Standard_Real    V,
                                      const Standard_Integer M,
                                      const Standard_Real    ParametricTolerance,
                                      const Standard_Boolean Add)
{
  TColStd_Array1OfReal k(1, 1);
  k(1) = V;
  TColStd_Array1OfInteger m(1, 1);
  m(1) = M;
  InsertVKnots(k, m, ParametricTolerance, Add);
}

//=================================================================================================

void Geom_BSplineSurface::IncrementUMultiplicity(const Standard_Integer FromI1,
                                                 const Standard_Integer ToI2,
                                                 const Standard_Integer Step)
{
  Handle(TColStd_HArray1OfReal) tk = uknots;
  TColStd_Array1OfReal          k((uknots->Array1())(FromI1), FromI1, ToI2);
  TColStd_Array1OfInteger       m(FromI1, ToI2);
  m.Init(Step);
  InsertUKnots(k, m, Epsilon(1.));
}

//=================================================================================================

void Geom_BSplineSurface::IncrementVMultiplicity(const Standard_Integer FromI1,
                                                 const Standard_Integer ToI2,
                                                 const Standard_Integer Step)
{
  Handle(TColStd_HArray1OfReal) tk = vknots;
  TColStd_Array1OfReal          k((vknots->Array1())(FromI1), FromI1, ToI2);

  TColStd_Array1OfInteger m(FromI1, ToI2);
  m.Init(Step);

  InsertVKnots(k, m, Epsilon(1.));
}

//=================================================================================================

void Geom_BSplineSurface::UpdateUKnots()
{

  Standard_Integer MaxKnotMult = 0;
  BSplCLib::KnotAnalysis(udeg,
                         uperiodic,
                         uknots->Array1(),
                         umults->Array1(),
                         uknotSet,
                         MaxKnotMult);

  if (uknotSet == GeomAbs_Uniform && !uperiodic)
  {
    ufknots = uknots;
  }
  else
  {
    ufknots =
      new TColStd_HArray1OfReal(1, BSplCLib::KnotSequenceLength(umults->Array1(), udeg, uperiodic));

    BSplCLib::KnotSequence(uknots->Array1(),
                           umults->Array1(),
                           udeg,
                           uperiodic,
                           ufknots->ChangeArray1());
  }

  if (MaxKnotMult == 0)
    Usmooth = GeomAbs_CN;
  else
  {
    switch (udeg - MaxKnotMult)
    {
      case 0:
        Usmooth = GeomAbs_C0;
        break;
      case 1:
        Usmooth = GeomAbs_C1;
        break;
      case 2:
        Usmooth = GeomAbs_C2;
        break;
      case 3:
        Usmooth = GeomAbs_C3;
        break;
      default:
        Usmooth = GeomAbs_C3;
        break;
    }
  }
}

//=================================================================================================

void Geom_BSplineSurface::UpdateVKnots()
{
  Standard_Integer MaxKnotMult = 0;
  BSplCLib::KnotAnalysis(vdeg,
                         vperiodic,
                         vknots->Array1(),
                         vmults->Array1(),
                         vknotSet,
                         MaxKnotMult);

  if (vknotSet == GeomAbs_Uniform && !vperiodic)
  {
    vfknots = vknots;
  }
  else
  {
    vfknots =
      new TColStd_HArray1OfReal(1, BSplCLib::KnotSequenceLength(vmults->Array1(), vdeg, vperiodic));

    BSplCLib::KnotSequence(vknots->Array1(),
                           vmults->Array1(),
                           vdeg,
                           vperiodic,
                           vfknots->ChangeArray1());
  }

  if (MaxKnotMult == 0)
    Vsmooth = GeomAbs_CN;
  else
  {
    switch (vdeg - MaxKnotMult)
    {
      case 0:
        Vsmooth = GeomAbs_C0;
        break;
      case 1:
        Vsmooth = GeomAbs_C1;
        break;
      case 2:
        Vsmooth = GeomAbs_C2;
        break;
      case 3:
        Vsmooth = GeomAbs_C3;
        break;
      default:
        Vsmooth = GeomAbs_C3;
        break;
    }
  }
}

//=======================================================================
// function : Normalizes the parameters if the curve is periodic
// purpose  : that is compute the cache so that it is valid
//=======================================================================

void Geom_BSplineSurface::PeriodicNormalization(Standard_Real& Uparameter,
                                                Standard_Real& Vparameter) const
{
  Standard_Real Period, aMaxVal, aMinVal;

  if (uperiodic)
  {
    aMaxVal           = ufknots->Value(ufknots->Upper() - udeg);
    aMinVal           = ufknots->Value(udeg + 1);
    Standard_Real eps = Abs(Epsilon(Uparameter));
    Period            = aMaxVal - aMinVal;

    if (Period <= eps)
      throw Standard_OutOfRange(
        "Geom_BSplineSurface::PeriodicNormalization: Uparameter is too great number");

    Standard_Boolean isLess, isGreater;
    isLess    = aMinVal - Uparameter > 0;
    isGreater = Uparameter - aMaxVal > 0;
    if (isLess || isGreater)
    {
      Standard_Real aDPar, aNbPer;
      aDPar = (isLess) ? (aMaxVal - Uparameter) : (aMinVal - Uparameter);
      modf(aDPar / Period, &aNbPer);
      Uparameter += aNbPer * Period;
    }
  }
  if (vperiodic)
  {
    aMaxVal           = vfknots->Value(vfknots->Upper() - vdeg);
    aMinVal           = vfknots->Value(vdeg + 1);
    Standard_Real eps = Abs(Epsilon(Vparameter));
    Period            = aMaxVal - aMinVal;

    if (Period <= eps)
      throw Standard_OutOfRange(
        "Geom_BSplineSurface::PeriodicNormalization: Vparameter is too great number");

    Standard_Boolean isLess, isGreater;
    isLess    = aMinVal - Vparameter > 0;
    isGreater = Vparameter - aMaxVal > 0;
    if (isLess || isGreater)
    {
      Standard_Real aDPar, aNbPer;
      aDPar = (isLess) ? (aMaxVal - Vparameter) : (aMinVal - Vparameter);
      modf(aDPar / Period, &aNbPer);
      Vparameter += aNbPer * Period;
    }
  }
}

//=================================================================================================

void Geom_BSplineSurface::SetWeight(const Standard_Integer UIndex,
                                    const Standard_Integer VIndex,
                                    const Standard_Real    Weight)
{
  if (Weight <= gp::Resolution())
    throw Standard_ConstructionError("Geom_BSplineSurface::SetWeight: Weight too small");
  TColStd_Array2OfReal& Weights = weights->ChangeArray2();
  if (UIndex < 1 || UIndex > Weights.ColLength() || VIndex < 1 || VIndex > Weights.RowLength())
  {
    throw Standard_OutOfRange("Geom_BSplineSurface::SetWeight: Index and #pole mismatch");
  }
  Weights(UIndex + Weights.LowerRow() - 1, VIndex + Weights.LowerCol() - 1) = Weight;
  Rational(Weights, urational, vrational);
}

//=================================================================================================

void Geom_BSplineSurface::SetWeightCol(const Standard_Integer      VIndex,
                                       const TColStd_Array1OfReal& CPoleWeights)
{
  TColStd_Array2OfReal& Weights = weights->ChangeArray2();
  if (VIndex < 1 || VIndex > Weights.RowLength())
  {
    throw Standard_OutOfRange("Geom_BSplineSurface::SetWeightCol: Index and #pole mismatch");
  }
  if (CPoleWeights.Lower() < 1 || CPoleWeights.Lower() > Weights.ColLength()
      || CPoleWeights.Upper() < 1 || CPoleWeights.Upper() > Weights.ColLength())
  {
    throw Standard_ConstructionError("Geom_BSplineSurface::SetWeightCol: invalid array dimension");
  }
  Standard_Integer I = CPoleWeights.Lower();
  while (I <= CPoleWeights.Upper())
  {
    if (CPoleWeights(I) <= gp::Resolution())
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetWeightCol: Weight too small");
    }
    Weights(I + Weights.LowerRow() - 1, VIndex + Weights.LowerCol() - 1) = CPoleWeights(I);
    I++;
  }
  // Verifie si c'est rationnel
  Rational(Weights, urational, vrational);
}

//=================================================================================================

void Geom_BSplineSurface::SetWeightRow(const Standard_Integer      UIndex,
                                       const TColStd_Array1OfReal& CPoleWeights)
{
  TColStd_Array2OfReal& Weights = weights->ChangeArray2();
  if (UIndex < 1 || UIndex > Weights.ColLength())
  {
    throw Standard_OutOfRange("Geom_BSplineSurface::SetWeightRow: Index and #pole mismatch");
  }
  if (CPoleWeights.Lower() < 1 || CPoleWeights.Lower() > Weights.RowLength()
      || CPoleWeights.Upper() < 1 || CPoleWeights.Upper() > Weights.RowLength())
  {

    throw Standard_ConstructionError("Geom_BSplineSurface::SetWeightRow: invalid array dimension");
  }
  Standard_Integer I = CPoleWeights.Lower();

  while (I <= CPoleWeights.Upper())
  {
    if (CPoleWeights(I) <= gp::Resolution())
    {
      throw Standard_ConstructionError("Geom_BSplineSurface::SetWeightRow: Weight too small");
    }
    Weights(UIndex + Weights.LowerRow() - 1, I + Weights.LowerCol() - 1) = CPoleWeights(I);
    I++;
  }
  // Verifie si c'est rationnel
  Rational(Weights, urational, vrational);
}

//=================================================================================================

void Geom_BSplineSurface::DumpJson(Standard_OStream& theOStream, Standard_Integer theDepth) const
{
  OCCT_DUMP_TRANSIENT_CLASS_BEGIN(theOStream)
  OCCT_DUMP_BASE_CLASS(theOStream, theDepth, Geom_BoundedSurface)

  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, urational)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vrational)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, uperiodic)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vperiodic)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, uknotSet)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vknotSet)

  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, Usmooth)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, Vsmooth)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, udeg)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vdeg)

  if (!poles.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, poles->Size())
  if (!weights.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, weights->Size())
  if (!ufknots.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, ufknots->Size())
  if (!vfknots.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vfknots->Size())

  if (!uknots.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, uknots->Size())
  if (!vknots.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vknots->Size())
  if (!umults.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, umults->Size())
  if (!vmults.IsNull())
    OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vmults->Size())

  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, umaxderivinv)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, vmaxderivinv)
  OCCT_DUMP_FIELD_VALUE_NUMERICAL(theOStream, maxderivinvok)
}
