unit Plan;

interface

(*
 * 25 jan 2012 - fixed serious bug: when analysis is performed,
 * the later changes of code matrix are not reflected by model's
 * parameters. The bug is shown only for a) custom plan, b) custom model.
 * The BVector was recalculating, but not BNames.
 *)

uses
  Windows, Messages, Controls, Classes, Dialogs, SysUtils, Forms, Grids,
  SharedData,
  Matrices,
  DistrDis,
  MTEdit,
  MiscRoutines,
  KNames,
  DlgNewY,
  ModelChange,
  ExProg,
  DlgNew,
  Step1, Step2, Step2a, Step3, Step3a, Step4, MathDef,
  CCO_CCR,
  CheckPoint,
  CComplete,SComplete,FComplete,FCComplete,
  TranslateDlg,
  TextReport,
  MiniDlg,
  ShowDlg,
  ArrangeDlg,
  AverageDlg,
  ExtrDlg,
  DiagStep3,
  ExPoint,
  GraphS,
  Setup,
  SharedCode;

type
  // Состояние данных
  TDataState =
  (
    dsUndeterminate,
    dsValid
  );
  // Метод поиска экстремума
  TMethodType =
  (
    mtScan,
    mtRandom,
    mtHaussZeidel
  );
  // Запросы серверу на совершение действий. Сами действия оформлены как
  // вызовы соответствующих методов.
  TPlanAction=
  (
    // Файл
    acNew,
    acOpen,
    acSave,
    acSaveAs,
    acClose,
    acCreateReport,
    // Импорт
    acImportAll,
    acImportCode,
    acImportNatural,
    acImportML,
    acImportDF,
    acImportY,
    // Экспорт
    acExportAll,
    acExportCode,
    acExportNatural,
    acExportML,
    acExportDF,
    acExportY,
    acExportReport,
    // Эксперимент
    acKNames,
    acCreateY,
    acCheckPoint,
    acAverageY,
    acAnalysis,
    acRegenerateCode,
    // Модель
    acExtremum,
    acTranslate,
    // Меню "Диаграмма"
    acNewGraph,
    acNewSurface,
    acExportGraph,
    acExportSurface,
    acImportGraph,
    acImportSurface,
    // Меню "Окно"
    acShowCode,
    acShowNatural,
    acShowML,
    acShowDF,
    acShowY,
    acShowReport,
    acShowDiagram,
    acHideCode,
    acHideNatural,
    acHideML,
    acHideDF,
    acHideY,
    acHideReport,
    acHideDiagram,
    acMinimizeForms,
    acShowForms,
    acArrangeForms
  );

// =======================================================================
// * * *                    СЕРВЕР МАТЕМАТИКИ                        * * *
// =======================================================================

type
  IMathPlan = class(TObject)
  // Функция возврата состояния - возможна ли указанная операция
    function        CanDo(Action:TPlanAction):LongBool;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Операции.
  // ---------------------------------------------------------------------
  // Меню "Файл"
    function        New:Boolean;virtual;stdcall;abstract;
    function        Open(szFile:PChar):Boolean;virtual;stdcall;abstract;
    function        SaveAs:Boolean;virtual;stdcall;abstract;
    function        Save:Boolean;virtual;stdcall;abstract;
    function        CreateReport:Boolean;virtual;stdcall;abstract;
    function        Close:Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Меню "Экспорт"
    function        ExportAll:Boolean;virtual;stdcall;abstract;
    function        ExportMainMatrix:Boolean;virtual;stdcall;abstract;
    function        ExportNaturalMatrix:Boolean;virtual;stdcall;abstract;
    function        ExportMLVector:Boolean;virtual;stdcall;abstract;
    function        ExportDFVector:Boolean;virtual;stdcall;abstract;
    function        ExportYMatrix:Boolean;virtual;stdcall;abstract;
    function        ExportReport:Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Меню "Импорт"
    function        ImportMainMatrix:Boolean;virtual;stdcall;abstract;
    function        ImportMLVector:Boolean;virtual;stdcall;abstract;
    function        ImportDFVector:Boolean;virtual;stdcall;abstract;
    function        ImportYMatrix:Boolean;virtual;stdcall;abstract;
    function        ImportAll:Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Меню "Эксперимент"
    function        SetKNames:Boolean;virtual;stdcall;abstract;
    function        NewData:Boolean;virtual;stdcall;abstract;
    function        CheckPoint:Boolean;virtual;stdcall;abstract;
    function        ShowAverageData:Boolean;virtual;stdcall;abstract;
  // Расчет средних, дисперсий, стандартных отклонений и критерия Кохрена
    function        CohrenAnalysis:Boolean;virtual;stdcall;abstract;
  // Регенерация кодовой матрицы
    function        RegenerateCode:Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Меню "Анализ"
    function        FullAnalysis:Boolean;virtual;stdcall;abstract;
  // Расчет  критерия Стьюдента; отсев коэффициентов, корректировка расши-
  // ренной матрицы плана и пересчет коэффициентов.
    function        StudentAnalysis:Boolean;virtual;stdcall;abstract;
  // Расчет невязок, дисперсии адекватности и критерия Фишера.
    function        FisherAnalysis:Boolean;virtual;stdcall;abstract;
    function        FindExtremum:Boolean;virtual;stdcall;abstract;
    function        Translate:Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Меню "Диаграммы"
    function        NewGraph:Boolean;virtual;stdcall;abstract;
    function        NewSurface:Boolean;virtual;stdcall;abstract;
    function        ExportGraph:Boolean;virtual;stdcall;abstract;
    function        ExportSurface:Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Меню "Окно"
    function        ShowMainMatrix:Boolean;virtual;stdcall;abstract;
    function        ShowNaturalMatrix:Boolean;virtual;stdcall;abstract;
    function        ShowMLVector:Boolean;virtual;stdcall;abstract;
    function        ShowDFVector:Boolean;virtual;stdcall;abstract;
    function        ShowYMatrix:Boolean;virtual;stdcall;abstract;
    function        ShowReport:Boolean;virtual;stdcall;abstract;
    function        HideMainMatrix:Boolean;virtual;stdcall;abstract;
    function        HideNaturalMatrix:Boolean;virtual;stdcall;abstract;
    function        HideMLVector:Boolean;virtual;stdcall;abstract;
    function        HideDFVector:Boolean;virtual;stdcall;abstract;
    function        HideYMatrix:Boolean;virtual;stdcall;abstract;
    function        HideReport:Boolean;virtual;stdcall;abstract;
    function        MinimizeForms:Boolean;virtual;stdcall;abstract;
    function        ShowForms:Boolean;virtual;stdcall;abstract;
    function        ArrangeForms(var Rc:TRect):Boolean;virtual;stdcall;abstract;
  // ---------------------------------------------------------------------
  // Были ли изменения
    function        Modified:Boolean;virtual;stdcall;abstract;
  // Синхронизация матриц
    procedure       SynchronizeMatrices(Sender:TObject);virtual;stdcall;abstract;
  // Использование новых установок
    procedure       OnChangeSetup;virtual;stdcall;abstract;
  // Возврат статуса (для статусной строки)
    function        GetStateString:PChar;virtual;stdcall;abstract;
    procedure       ClearStatus;virtual;stdcall;abstract;
  // Текущее имя файла (для заголовка)
    function        FileName:PChar;virtual;stdcall;abstract;
    function        GetGServer:pIGraphServer;virtual;stdcall;abstract;
  // Скрытие / показ форм (при изменении стиля главного окна)
    procedure       TemporaryClose(var c,n,m,d,y,r,g:Boolean);virtual;stdcall;abstract;
    procedure       RestoreVisibleForms(c,n,m,d,y,r,g:Boolean);virtual;stdcall;abstract;
  // Параметры плана
    procedure       GetKNR(K,N,R:pdword);virtual;stdcall;abstract;
  // Пересчет координат точек
    procedure       NaturalToCode(Dst,Src:PDoubleArray);virtual;stdcall;abstract;
    procedure       CodeToNatural(Dst,Src:PDoubleArray);virtual;stdcall;abstract;
  // Вычисление значения в точке кодового пространства
    procedure       GetY(Pt:PDoubleArray;Res:pdouble);virtual;stdcall;abstract;
  // Возврат дескриптора главной формы
    function        GetMainFormHandle : HWND;virtual;stdcall;abstract;
  // 23.11.99
    function        GetCurrentGen:PGen;virtual;stdcall;abstract;
    function        GetCurrentModel:PModel;virtual;stdcall;abstract;
    function        GetModelAdden(nAdden:integer):PAdden;virtual;stdcall;abstract;
    function        GetCurrentBVector:PDouble;virtual;stdcall;abstract;
  end;

// =======================================================================
// TMathPlan - реализация сервера
// =======================================================================

type
  TMathPlan = class(IMathPlan)
  // =====================================================================
  // Интерфейсная часть
  // =====================================================================
  public
  // Функция возврата состояния - возможна ли указанная операция
    function        CanDo(Action:TPlanAction):LongBool;override;
  // ---------------------------------------------------------------------
  // Операции.
  // ---------------------------------------------------------------------
  // Меню "Файл"
    function        New:Boolean;override;
    function        Open(szFile:PChar):Boolean;override;
    function        SaveAs:Boolean;override;
    function        Save:Boolean;override;
    function        CreateReport:Boolean;override;
    function        Close:Boolean;override;
  // ---------------------------------------------------------------------
  // Меню "Экспорт"
    function        ExportAll:Boolean;override;
    function        ExportMainMatrix:Boolean;override;
    function        ExportNaturalMatrix:Boolean;override;
    function        ExportMLVector:Boolean;override;
    function        ExportDFVector:Boolean;override;
    function        ExportYMatrix:Boolean;override;
    function        ExportReport:Boolean;override;
  // ---------------------------------------------------------------------
  // Меню "Импорт"
    function        ImportMainMatrix:Boolean;override;
    function        ImportMLVector:Boolean;override;
    function        ImportDFVector:Boolean;override;
    function        ImportYMatrix:Boolean;override;
    function        ImportAll:Boolean;override;
  // ---------------------------------------------------------------------
  // Меню "Эксперимент"
    function        SetKNames:Boolean;override;
    function        NewData:Boolean;override;
    function        CheckPoint:Boolean;override;
    function        ShowAverageData:Boolean;override;
  // Расчет средних, дисперсий, стандартных отклонений и критерия Кохрена
    function        CohrenAnalysis:Boolean;override;
  // Регенерация кодовой матрицы
    function        RegenerateCode:Boolean;override;
  // ---------------------------------------------------------------------
  // Меню "Анализ"
    function        FullAnalysis:Boolean;override;
  // Расчет  критерия Стьюдента;override; отсев коэффициентов, корректировка расши-
  // ренной матрицы плана и пересчет коэффициентов.
    function        StudentAnalysis:Boolean;override;
  // Расчет невязок, дисперсии адекватности и критерия Фишера.
    function        FisherAnalysis:Boolean;override;
    function        FindExtremum:Boolean;override;
    function        Translate:Boolean;override;
  // ---------------------------------------------------------------------
  // Меню "Диаграммы"
    function        NewGraph:Boolean;override;
    function        NewSurface:Boolean;override;
    function        ExportGraph:Boolean;override;
    function        ExportSurface:Boolean;override;
  // ---------------------------------------------------------------------
  // Меню "Окно"
    function        ShowMainMatrix:Boolean;override;
    function        ShowNaturalMatrix:Boolean;override;
    function        ShowMLVector:Boolean;override;
    function        ShowDFVector:Boolean;override;
    function        ShowYMatrix:Boolean;override;
    function        ShowReport:Boolean;override;
    function        HideMainMatrix:Boolean;override;
    function        HideNaturalMatrix:Boolean;override;
    function        HideMLVector:Boolean;override;
    function        HideDFVector:Boolean;override;
    function        HideYMatrix:Boolean;override;
    function        HideReport:Boolean;override;
    function        MinimizeForms:Boolean;override;
    function        ShowForms:Boolean;override;
    function        ArrangeForms(var Rc:TRect):Boolean;override;
  // ---------------------------------------------------------------------
  // Были ли изменения
    function        Modified:Boolean;override;
  // Синхронизация матриц
    procedure       SynchronizeMatrices(Sender:TObject);override;
  // Использование новых установок
    procedure       OnChangeSetup;override;
  // Возврат статуса (для статусной строки)
    function        GetStateString:PChar;override;
    procedure       ClearStatus;override;
  // Текущее имя файла (для заголовка)
    function        FileName:PChar;override;
    function        GetGServer:pIGraphServer;override;
  // Скрытие / показ форм (при изменении стиля главного окна)
    procedure       TemporaryClose(var c,n,m,d,y,r,g:Boolean);override;
    procedure       RestoreVisibleForms(c,n,m,d,y,r,g:Boolean);override;
  // Параметры плана
    procedure       GetKNR(K,N,R:pdword);override;
  // Пересчет координат точек
    procedure       NaturalToCode(Dst,Src:PDoubleArray);override;
    procedure       CodeToNatural(Dst,Src:PDoubleArray);override;
  // Вычисление значения в точке кодового пространства
    procedure       GetY(Pt:PDoubleArray;Res:pdouble);override;
  // Возврат ручки главной формы
    function        GetMainFormHandle:HWND;override;
  // 23.11.99
    function        GetCurrentGen:PGen;override;
    function        GetCurrentModel:PModel;override;
    function        GetModelAdden(nAdden:integer):PAdden;override;
    function        GetCurrentBVector:PDouble;override;
  // =====================================================================
  // Конец интерфейсной части
  // =====================================================================
  private
  // ---------------------------------------------------------------------
  // Данные плана
  // ---------------------------------------------------------------------
    dsDegree        : TDataState;
  // Тип плана
    dspType         : TDataState;
    pType           : TPlanType;
  // Используемые генератор, модель, слагаемые
    Gen             : PGen;
    Model           : PModel;
    Addens          : TList;
  // Полное количество экспериментов без учета повторений
    dsN             : TDataState;
    N               : integer;
  // Величина звездного плеча (для композиционных планов)
    dsAlpha         : TDataState;
    Alpha           : double;
  // Основная матрица плана
    dsMainMatrix    : TDataState;
    MainMatrix      : TStreamableMatrix;
  // Координаты контрольной точки (кодовые)
    dsCPoint        : TDataState;
    CPoint          : TStreamableVector;
  // Вектор основных уровней факторов
    dsMLVector      : TDataState;
    MLVector        : TStreamableVector;
  // Вектор интервалов варьирования факторов
    dsDFVector      : TDataState;
    DFVector        : TStreamableVector;
  // Основная матрица плана в натуральном выражении
    dsNaturalMatrix : TDataState;
    NaturalMatrix   : TStreamableMatrix;
  // Расширенная матрица плана
    dsExpandedMatrix: TDataState;
    ExpandedMatrix  : TStreamableMatrix;
  // Информационная матрица плана
    dsMMatrix       : TDataState;
    MMatrix         : TStreamableMatrix;
  // Ковариационная матрица плана
    dsCMatrix       : TDataState;
    CMatrix         : TStreamableMatrix;
  // Количество повторений каждого опыта
    dsR             : TDataState;
    R               : integer;
  // Количество повторений в контрольной точке
    dsRcp           : TDataState;
    Rcp             : integer;
  // Матрица наблюдений
    dsYMatrix       : TDataState;
    YMatrix         : TStreamableMatrix;
  // Експериментальное значение отклика в контрольной точке
    dsCPY           : TDataState;
    CPY             : TStreamableVector;
  // Вектор средних значений наблюдений
    dsAVector       : TDataState;
    AVector         : TStreamableVector;
  // Вектор дисперсий наблюдений
    dsDVector       : TDataState;
    DVector         : TStreamableVector;
  // Дисперсия предсказания
    dsDfore         : TDataState;
    Dfore           : double;
  // Дисперсия воспроизводимости
    dsDV            : TDataState;
    DV              : double;
  // Вектор средневкадратичных отклонений наблюдений
    dsSVector       : TDataState;
    SVector         : TStreamableVector;
  // Максимальная из дисперсий наблюдений
    dsDMax          : TDataState;
    DMax            : double;
  // 10.08.2000
  // Вектор, элементы которого равны True если соответствующее слагаемое
  // модели является значимым
  	AddenUsed  		: TList;
  // Вектор коэффициентов модели
    dsBVector       : TDataState;
    BVector         : TStreamableVector;
  // Символические имена коэффициентов модели (напрмер: B0, B11, и т.д.)
    dsBNames        : TDataState;
    BNames          : TStringList;
  // Названия факторов
    dsKNames        : TDataState;
    KNames          : TStringList;
  // Название отклика
    NameY           : string;
  // Число значимых коэффициентов
    dsActualBSize   : TDataState;
    ActualBSize     : integer;
  // Вектор квадратов невязок
    dsDYVector      : TDataState;
    DYVector        : TStreamableVector;
  // Дисперcия адекватности
    dsDad           : TDataState;
    Dad             : double;
  // Расчетное значение критерия Кохрена
    dsCH_R          : TDataState;
    CH_R            : double;
  // Расчетное значение критерия Фишера
    dsFS_R          : TDataState;
    FS_R            : double;
  // Табличное значение критерия Кохрена
    dsCH_T          : TDataState;
    CH_T            : double;
  // Табличное значение критерия Фишера
    dsFS_T          : TDataState;
    FS_T            : double;
  // Растчетное значение критерия Стьюдента (для контрольной точки)
    dsST_R          : TDataState;
    ST_R            : double;
  // Табличное значение критерия Стьюдента (для контрольной точки)
    dsST_T          : TDataState;
    ST_T            : double;
  // Точка оптимума
    dsMainPoint     : TDataState;
    MainPoint       : TStreamableVector;
  // ---------------------------------------------------------------------
    OtherModified   : Boolean;
  // Панели редактирования
    CMForm          : TMTEditForm;
    MLForm          : TMTEditForm;
    DFForm          : TMTEditForm;
    NMForm          : TMTEditForm;
    YForm           : TMTEditForm;
  // Заголовки панелей
    FormNames       : array[0..6] of string;
  // Количество факторов
    __Degree        : integer;
  // Статус
    StatusString    : string;
  // Диалоги открытия и сохранения
    OpenDialog      : TOpenDialog;
    SaveDialog      : TSaveDialog;
    OpenResult      : Boolean;
    SaveResult      : Boolean;
  // Фильтры для диалогов
    StrTXTFilter    : string;
    StrGRDFilter    : string;
    StrMPLFilter    : string;
  // Сервер графики
    pIGServer       : pIGraphServer;
  // Имена файлов: основного и блоков данных
    CurrentFile: string;
    CMName     : string;
    NMName     : string;
    MLName     : string;
    DFName     : string;
    YName      : string;
  // Создение панелей
    procedure       CreateCMForm;
    procedure       CreateNMForm;
    procedure       CreateMLForm;
    procedure       CreateDFForm;
    procedure       CreateYForm;
    procedure       CreateReportForm;
  // Уничтожение панелей
    procedure       DestroyCMForm;
    procedure       DestroyNMForm;
    procedure       DestroyMLForm;
    procedure       DestroyDFForm;
    procedure       DestroyYForm;
    procedure       DestroyReportForm;
    procedure       DestroyForms;
  // Обработчики
    procedure       CheckPointDlgClose(Sender:TObject;
      var Action:TCloseAction);
  // Пересчет матриц
    procedure       NaturalFromCode;
  // Расширение кодовой матрицы
    function        ExpandCodeMatrix:Boolean;
  // Расширение кодовой матрицы встроенного плана
    procedure       ExpandCodeMatrixDefault;
  // Расширение вектора
    procedure       ExpandCPoint(Dst,Src:TVector);
  // Расширение вектора (для встроенного плана)
    procedure       ExpandCPointDefault(Dst,Src:TVector);
  // Заполнение списка именами коэффициентов
    procedure       FillBNames;
  // Возвращает величину звездного плеча встроенного композиционного плана
    function        GetAlpha(pT:TPlanType):double;
  // Доступ к данным
    function        Degree:integer;
    function        SetDegree(Deg:integer):Boolean;
  // Очистка всех полей, установка флагов в состояние "неопределенно"
    procedure       CleanFields;
  // Освобождение памяти, занятой полями
    procedure       ReleaseAll;
  // Создание нового плана
    function        NewPlan:Boolean;
  // Создание матриц
    procedure       CreateCodeMatrix;
    function        CreateYMatrix:Boolean;
  // Экспорт/импорт матриц
    function        LoadMainMatrix(FileName:string):Boolean;
    function        LoadMLVector(FileName:string):Boolean;
    function        LoadDFVector(FileName:string):Boolean;
    function        LoadYMatrix(FileName:string):Boolean;
  // Загрузка/выгрузка всех данных
    function        StreamSave(var Stream:TStream):Boolean;
    function        StreamLoad(var Stream:TStream):Boolean;
  // ---------------------------------------------------------------------
  // Методы показывают/скрывают формы, в которых ведется редактирование
  // ---------------------------------------------------------------------
    procedure       EditCodeMatrix(OkCancel:Boolean);
    procedure       EditMLVector(OkCancel:Boolean);
    procedure       EditDFVector(OkCancel:Boolean);
    procedure       EditNaturalMatrix(OkCancel:Boolean);
    procedure       EditYMatrix(OkCancel:Boolean);
    procedure       SetFormEditing(Name:string;Editing:Boolean);
    procedure       ShowDataForms;
  // Генерация имен факторов
    procedure       GenerateDeafultKNames;
  // Поиск экстремальных значений
    procedure       FindMin(Start, Stop : TVector; CusData : integer;
      UseOld:Boolean;Method : TMethodType);
    procedure       FindMax(Start, Stop : TVector; CusData : integer;
      UseOld:Boolean;Method : TMethodType);
  // Составление отчета
    procedure       Report(var Stream:TStream);
  // Вывод диалога открытия файла
    function        OpenExec:Boolean;
  // Вывод диалога сохранения файла
    function        SaveExec:Boolean;
    procedure       NothingModified;
  // Установка статуса
    function        Success(Action:TPlanAction):Boolean;
    function        Fail(Action:TPlanAction):Boolean;
  // подготовка данных для статистического анализа
	function		BeforeCohrenAnalysis:Boolean;
    {
	function		BeforeStudentAnalysis:Boolean;
	function		BeforeFisherAnalysis:Boolean;
    }
  // ---------------------------------------------------------------------
  // Создание и уничтожение
  // ---------------------------------------------------------------------
  public
    constructor     Create;
    procedure       Cleanup;
    destructor      Destroy;override;
  // Пересчет координат точек
    procedure       NtoC(var Dst,Src:TVector);
    procedure       CtoN(var Dst,Src:TVector);
  // Вычисление значения в точке кодового пространства (БЕЗ ПРОВЕРОК)
    function        GetYC(var V:TVector):double;
  // Заполнение списка форматированными строками с именами факторов
    procedure       LBFillWithNames(List:TStrings);
  end;

// =======================================================================
// TMathPlanData - копия данных плана. Используется в операциях  отмены  и
// при сохранении / загрузке.
// =======================================================================

type
  TMathPlanData  = object
  private
    DataState    : TDataState;

    pType        : TPlanType;
    PlanMatrix   : TStreamableMatrix;
    CPointPresent: Boolean;
    CPoint       : TStreamableVector;
    CPY          : TStreamableVector;
    YMatrix      : TStreamableMatrix;
    MLVector     : TStreamableVector;
    DFVector     : TStreamableVector;
    GenDesc      : string;
    ModelDesc    : string;
    AddensDesc   : TStringList;
    KNames       : TStringList;
    NameY        : string;
  public
    constructor  Create;
    destructor   Destroy;
    function     MakeCopy(var LivingObject:TMathPlan):Boolean;
    function     SaveToStream(var Stream:TStream):Boolean;
    function     LoadFromStream(var Stream:TStream):Boolean;
    function     EnliveObject(var DeadObject:TMathPlan):Boolean;
  end;

// =======================================================================
// * * *                                                             * * *
// =======================================================================

implementation

uses MainUnit;

// -----------------------------------------------------------------------
// CanDo
// -----------------------------------------------------------------------

function TMathPlan.CanDo;
begin
  // Исходное состояние - ничего сделать нельзя
  Result:=False;
  case Action of
    // -------------------------------------------------------------------
    acNew:
    begin
      Result:=dsDegree=dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acOpen:
    begin
      Result:=CanDo(acNew);
    end;
    // -------------------------------------------------------------------
    acSave:
    begin
//      Result := CanDo(acSaveAs) and (CurrentFile <> '');
      // 05.09.2011, kkatarn
      Result := CanDo(acSaveAs);
    end;
    // -------------------------------------------------------------------
    acSaveAs:
    begin
      Result:=CanDo(acExportCode) and CanDo(acExportNatural) and
        CanDo(acExportML) and CanDo(acExportDF) and CanDo(acExportY);
    end;
    // -------------------------------------------------------------------
    acExportCode:
    begin
      Result:=dsMainMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acExportNatural:
    begin
      Result:=dsNaturalMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acExportML:
    begin
      Result:=dsMLVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acExportDF:
    begin
      Result:=dsDFVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acExportY:
    begin
      Result:=dsYMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acExportReport:
    begin
      Result:=ReportForm<>nil;
    end;
    // -------------------------------------------------------------------
    acImportCode:
    begin
      Result:=True;
    end;
    // -------------------------------------------------------------------
    acImportNatural:
    begin
      Result:=dsNaturalMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acImportML:
    begin
      Result:=dsMLVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acImportDF:
    begin
      Result:=dsDFVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acImportY:
    begin
      Result:=dsMainMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acExportAll:
    begin
      Result:=(CanDo(acExportCode))and(CanDo(acExportNatural))and
        (CanDo(acExportML))and(CanDo(acExportDF))and
        (CanDo(acExportY));
    end;
    // -------------------------------------------------------------------
    acImportAll:
    begin
      Result:=CanDo(acNew);
    end;
    // -------------------------------------------------------------------
    acClose:
    begin
      // ВНИМАНИЕ !!! Этот код предназначен ТОЛЬКО для управления меню.
      Result:=dsDegree<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acCreateReport:
    begin
      Result:=(dsDegree<>dsUndeterminate)and(dspType<>dsUndeterminate);
    end;
    // -------------------------------------------------------------------
    acKNames:
    begin
      Result:=dsR<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acCreateY:
    begin
      Result:=dsMainMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acCheckPoint:
    begin
      Result:=dsDegree<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acAverageY:
    begin
      Result:=dsYMatrix<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acAnalysis:
    begin
      Result:=(dsMainMatrix=dsValid) and (dsYMatrix=dsValid);
    end;
    // -------------------------------------------------------------------
    acRegenerateCode:
    begin
      Result:=(dspType<>dsUndeterminate)
        and (dsDegree<>dsUndeterminate) and (dsN<>dsUndeterminate);
      if Result then
      begin
        if pType = ptFromWizard then
          Result:=Gen<>nil
        else
          Result:=pType in [ptFFE..ptFromWizard];
      end;
    end;
    // -------------------------------------------------------------------
    acExtremum:
    begin
      Result:=dsBVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acTranslate:
    begin
      Result:=
        ((dsMLVector<>dsUndeterminate)and(dsDFVector<>dsUndeterminate))or
        (dsBVector<>dsUndeterminate);
    end;
    // -------------------------------------------------------------------
    acNewGraph:
    begin
      Result:=dsBVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acNewSurface:
    begin
      Result:=(dsBVector<>dsUndeterminate)and(Degree>1);
    end;
    // -------------------------------------------------------------------
    acExportGraph:
    begin
      Result:=dsBVector<>dsUndeterminate;
    end;
    // -------------------------------------------------------------------
    acImportGraph:
    begin
      Result:=(GetGServer<>nil)and(GetGServer^.CanDo(acImport));
    end;
    // -------------------------------------------------------------------
    acExportSurface:
    begin
      Result:=(dsBVector<>dsUndeterminate)and(Degree>1);
    end;
    // -------------------------------------------------------------------
    acImportSurface:
    begin
      Result:=(GetGServer<>nil)and(GetGServer^.CanDo(acImport));
    end;
    // -------------------------------------------------------------------
    acShowCode:
    begin
      Result:=(CMForm<>nil)and(CMForm.WindowState=wsMinimized);
    end;
    // -------------------------------------------------------------------
    acShowNatural:
    begin
      Result:=(NMForm<>nil)and(NMForm.WindowState=wsMinimized);
    end;
    // -------------------------------------------------------------------
    acShowML:
    begin
      Result:=(MLForm<>nil)and(MLForm.WindowState=wsMinimized);
    end;
    // -------------------------------------------------------------------
    acShowDF:
    begin
      Result:=(DFForm<>nil)and(DFForm.WindowState=wsMinimized);
    end;
    // -------------------------------------------------------------------
    acShowY:
    begin
      Result:=(YForm<>nil)and(YForm.WindowState=wsMinimized);
    end;
    // -------------------------------------------------------------------
    acShowReport:
    begin
      Result:=(ReportForm<>nil)and(ReportForm.WindowState=wsMinimized);
    end;
    // -------------------------------------------------------------------
    acShowDiagram:
    begin
      Result:=(GetGServer<>nil)and(GetGServer^.CanDo(acShow));
    end;
    // -------------------------------------------------------------------
    acHideCode:
    begin
      Result:=(CMForm<>nil)and(CMForm.WindowState<>wsMinimized);
    end;
    // -------------------------------------------------------------------
    acHideNatural:
    begin
      Result:=(NMForm<>nil)and(NMForm.WindowState<>wsMinimized);
    end;
    // -------------------------------------------------------------------
    acHideML:
    begin
      Result:=(MLForm<>nil)and(MLForm.WindowState<>wsMinimized);
    end;
    // -------------------------------------------------------------------
    acHideDF:
    begin
      Result:=(DFForm<>nil)and(DFForm.WindowState<>wsMinimized);
    end;
    // -------------------------------------------------------------------
    acHideY:
    begin
      Result:=(YForm<>nil)and(YForm.WindowState<>wsMinimized);
    end;
    // -------------------------------------------------------------------
    acHideReport:
    begin
      Result:=(ReportForm<>nil)and(ReportForm.WindowState<>wsMinimized);
    end;
    // -------------------------------------------------------------------
    acHideDiagram:
    begin
      Result:=(GetGServer<>nil)and(GetGServer^.CanDo(acHide));
    end;
    // -------------------------------------------------------------------
    acMinimizeForms:
    begin
      Result:=CanDo(acHideCode) or CanDo(acHideNatural) or
        CanDo(acHideML) or CanDo(acHideDF) or CanDo(acHideY) or
        CanDo(acHideReport) or CanDo(acHideDiagram);
    end;
    // -------------------------------------------------------------------
    acShowForms:
    begin
      Result:=CanDo(acShowCode) or CanDo(acShowNatural) or
        CanDo(acShowML) or CanDo(acShowDF) or CanDo(acShowY) or
        CanDo(acShowReport) or CanDo(acShowDiagram);
    end;
    // -------------------------------------------------------------------
    acArrangeForms:
    begin
      // Если все окна скрыты, то упорядочивать нечего
      Result:=CanDo(acMinimizeForms);
    end;
    // -------------------------------------------------------------------
    else
    begin
      Warning('MathPlan.CanDo(): Запрос на неизвестное действие ("'+
        IntToStr(Ord(Action))+'").');
    end;
    // -------------------------------------------------------------------
  end;
end;

function TMathPlan.Success;
begin
  Result:=True;
  case Action of
    acNew:    StatusString:='Создание плана завершено.';
    acOpen:   StatusString:='Открытие плана завершено.';
    acSaveAs, acSave: StatusString:='Сохранение плана завершено.';
    acExportCode:    StatusString:='Кодовая матрица сохранена.';
    acExportNatural: StatusString:='Матрица плана сохранена.';
    acExportML: StatusString:='Вектор основных уровней сохранен.';
    acExportDF: StatusString:='Вектор интервалов варьирования сохранен.';
    acExportY:  StatusString:='Матрица наблюдений сохранена.';
    acExportReport:  StatusString:='Отчет сохранен.';
    acImportCode:    StatusString:='Кодовая матрица загружена.';
    acImportNatural: StatusString:='Матрица плана загружена.';
    acImportML: StatusString:='Вектор основных уровней загружен.';
    acImportDF: StatusString:='Вектор интервалов варьирования загружен.';
    acImportY:  StatusString:='Матрица наблюдений загружена.';
    acExportAll: StatusString:='Данные сохранены.';
    acImportAll: StatusString:='Данные встроенного плана загружены.';
    acClose: StatusString:='Данные удалены из памяти.';
    acCreateReport: StatusString:='Отчет создан.';
    acKNames:   StatusString:='Определены новые имена факторов.';
    acCreateY:  StatusString:='Создана матрица наблюдений.';
    acAnalysis:   StatusString:='Проведен полный анализ.';
    acRegenerateCode: StatusString:='Установлена исходная кодовая матрица.';
    acExtremum: StatusString:='Проведен поиск экстремума.';
    acExportGraph: StatusString:='Сохранены данные для графика.';
    acExportSurface: StatusString:='Сохранены данные для поверхности.';
    else ClearStatus; //StatusString:='Операция успешно завершена.';
  end;
end;

function TMathPlan.Fail;
begin
  Result:=False;
  StatusString:='Операция не завершена.';
end;

procedure TMathPlan.ClearStatus;
begin
  StatusString:='';
end;

procedure TMathPlan.Cleanup;
begin
  ReleaseAll;
  CleanFields;
end;

function TMathPlan.New:Boolean;
begin
  Result:=Fail(acNew);
  if((not CanDo(acNew))or(not NewPlan)) then exit;
  Result:=Success(acNew);
  CurrentFile:='';
  MainForm.StateChanged;
end;

function TMathPlan.Open;
var
  S : TFileStream;
begin
  Result:=Fail(acOpen);
  if(not CanDo(acOpen)) then exit;
  if(szFile=nil) then
  begin
    OpenDialog.Title := 'Открыть файл планирования';
    OpenDialog.Filter := StrMPLFilter;
    OpenDialog.FileName := CurrentFile;
    if(not OpenExec) then exit;
  end
  else
    OpenDialog.FileName:=StrPas(szFile);
  try
    S:=TFileStream.Create(OpenDialog.FileName,fmOpenRead);
    if(StreamLoad(TStream(S))) then
    begin
      Result:=Success(acOpen);
      CurrentFile:=OpenDialog.FileName;
      MainForm.StateChanged;
    end;
    S.Destroy;
  except
    on EFOpenError do
    begin
      Error('Ошибка открытия файла "'+OpenDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
    on EReadError do
    begin
      Error('Ошибка чтения файла "'+OpenDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.SaveAs;
var
  TmpName : string;
begin
  Result:=Fail(acSaveAs);
  if(not CanDo(acSaveAs)) then exit;
  SaveDialog.Title := 'Сохранить как новый файл планирования';
  SaveDialog.Filter := StrMPLFilter;
  SaveDialog.FileName := CurrentFile;
  if(not SaveExec) then exit;
  TmpName:=CurrentFile;
  CurrentFile:=SaveDialog.FileName;
  if(not Save) then
  begin
    CurrentFile:=TmpName;
    exit;
  end;
  MainForm.StateChanged;
  Result:=Success(acSaveAs);
end;

function TMathPlan.Save;
var
  S : TFileStream;
begin
  Result := Fail(acSave);
  if(not CanDo(acSave)) then exit;
  if CurrentFile = '' then
  begin
    Result := Self.SaveAs;
    Exit;
  end;
  try
    S:=TFileStream.Create(CurrentFile,fmCreate);
    Result:=StreamSave(TStream(S));
    S.Destroy;
    if(Result) then
    begin
      Success(acSave);
      MainForm.StateChanged;
    end
    else
      if(FileExists(CurrentFile)) then DeleteFile(CurrentFile);
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+CurrentFile+'". Извините.');
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+CurrentFile+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.ExportMainMatrix;
var
  S : TFileStream;
begin
  Result:=Fail(acExportCode);
  if(not CanDo(acExportCode)) then exit;
  SaveDialog.Title:='Экспорт кодовой матрицы';
  SaveDialog.Filter := StrTXTFilter;
  SaveDialog.FileName:=CMName;
  if(not SaveExec) then exit;
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    MainMatrix.SaveToStreamAsText(TStream(S));
    Result:=Success(acExportCode);
    CMName:=SaveDialog.FileName;
    S.Destroy;
    MainForm.StateChanged;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.ExportNaturalMatrix;
var
  S : TFileStream;
begin
  Result:=Fail(acExportNatural);
  if(not CanDo(acExportNatural)) then
    exit;
  SaveDialog.Title:='Экспорт матрицы в натуральном выражении';
  SaveDialog.Filter:=StrTXTFilter;
  SaveDialog.FileName:=NMName;
  if(not SaveExec) then exit;
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    NaturalMatrix.SaveToStreamAsText(TStream(S));
    Result:=Success(acExportNatural);
    NMName:=SaveDialog.FileName;
    S.Destroy;
    MainForm.StateChanged;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.ExportMLVector;
var
  S : TFileStream;
begin
  Result:=Fail(acExportML);
  if(not CanDo(acExportML)) then
    exit;
  SaveDialog.Title:='Экспорт вектора основных уровней';
  SaveDialog.Filter:=StrTXTFilter;
  SaveDialog.FileName:=MLName;
  if(not SaveExec) then exit;
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    MLVector.SaveToStreamAsText(TStream(S));
    Result:=Success(acExportML);
    MLName:=SaveDialog.FileName;
    S.Destroy;
    MainForm.StateChanged;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.ExportDFVector;
var
  S : TFileStream;
begin
  Result:=Fail(acExportDF);
  if(not CanDo(acExportDF)) then
    exit;
  SaveDialog.Title:='Экспорт вектора интервалов варьирования';
  SaveDialog.Filter := StrTXTFilter;
  SaveDialog.FileName:=DFName;
  if(not SaveExec) then exit;
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    DFVector.SaveToStreamAsText(TStream(S));
    Result:=Success(acExportDF);
    DFName:=SaveDialog.FileName;
    S.Destroy;
    MainForm.StateChanged;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.ExportYMatrix;
var
  S : TFileStream;
begin
  Result:=Fail(acExportY);
  if(not CanDo(acExportY)) then
    exit;
  SaveDialog.Title:='Экспорт матрицы откликов';
  SaveDialog.Filter:=StrTXTFilter;
  SaveDialog.FileName:=YName;
  if(not SaveExec) then exit;
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    YMatrix.SaveToStreamAsText(TStream(S));
    Result:=Success(acExportY);
    YName:=SaveDialog.FileName;
    S.Destroy;
    MainForm.StateChanged;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      exit;
    end;
  end;
end;

function TMathPlan.ExportReport;
begin
  Result:=Fail(acExportReport);
  if(not CanDo(acExportReport)) then exit;
  if(ReportForm.Save) then
    Result:=Success(acExportReport);
end;

function TMathPlan.ExportAll;
begin
  Result:=Fail(acExportAll);
  if(CanDo(acExportCode)) then
    if(not ExportMainMatrix) then exit;
  if(CanDo(acExportNatural)) then
    if(not ExportNaturalMatrix) then exit;
  if(CanDo(acExportML)) then
    if(not ExportMLVector) then exit;
  if(CanDo(acExportDF)) then
    if(not ExportDFVector) then exit;
  if(CanDo(acExportY)) then
    if(not ExportYMatrix) then exit;
  Result:=Success(acExportAll);
end;

function TMathPlan.ImportMainMatrix:Boolean;
begin
  Result:=Fail(acImportCode);
  if(not CanDo(acImportCode)) then exit;
  OpenDialog.Title:='Импорт кодовой матрицы';
  OpenDialog.Filter:=StrTXTFilter;
  OpenDialog.FileName:=CMName;
  if((not OpenExec) or (not LoadMainMatrix(OpenDialog.FileName))) then
    exit;
  CMName:=OpenDialog.FileName;
  Result:=Success(acImportCode);
  MainForm.StateChanged;
end;

function TMathPlan.ImportMLVector;
begin
  Result:=Fail(acImportML);
  if(not CanDo(acImportML)) then exit;
  OpenDialog.Title:='Импорт вектора основных уровней';
  OpenDialog.Filter:=StrTXTFilter;
  OpenDialog.FileName:=MLName;
  if(not OpenExec) then exit;
  if(not LoadMLVector(OpenDialog.FileName)) then exit;
  MLName:=OpenDialog.FileName;
  Result:=Success(acImportML);
  MainForm.StateChanged;
end;

function TMathPlan.ImportDFVector;
begin
  Result:=Fail(acImportDF);
  if(not CanDo(acImportDF)) then exit;
  OpenDialog.Title:='Импорт вектора интервалов варьирования';
  OpenDialog.Filter:=StrTXTFilter;
  OpenDialog.FileName:=MLName;
  if(not OpenExec) then exit;
  if(not LoadDFVector(OpenDialog.FileName)) then exit;
  DFName:=OpenDialog.FileName;
  Result:=Success(acImportDF);
  MainForm.StateChanged;
end;

function TMathPlan.ImportYMatrix;
begin
  Result:=Fail(acImportY);
  if(not CanDo(acImportY)) then exit;
  OpenDialog.Title:='Импорт матрицы откликов';
  OpenDialog.Filter:=StrTXTFilter;
  OpenDialog.FileName:=YName;
  if(not OpenExec) then exit;
  if(not LoadYMatrix(OpenDialog.FileName)) then exit;
  YName:=OpenDialog.FileName;
  Result:=Success(acImportY);
  MainForm.StateChanged;
end;

function TMathPlan.ImportAll;
var
 lr : Boolean;
begin
  Result:=Fail(acImportAll);
  if(not CanDo(acImportCode)) then exit;
  repeat
    lr:=ImportMainMatrix;
    if(not OpenResult) then exit;
  until lr;
  if(not CanDo(acImportML)) then exit;
  repeat
    lr:=ImportMLVector;
    if(not OpenResult) then exit;
  until lr;
  if(not CanDo(acImportDF)) then exit;
  repeat
    lr:=ImportDFVector;
    if(not OpenResult) then exit;
  until lr;
  if(not CanDo(acImportY)) then exit;
  if(AskYN('Загрузить таблицу откликов - ?')=idYes) then
  begin
    repeat
      lr:=ImportYMatrix;
      if(not OpenResult) then exit;
    until lr;
  end;
  // Мы импорировали только матрицы. Генерируем имена факторов:
  GenerateDeafultKNames;
  Result:=Success(acImportAll);
end;

function TMathPlan.Modified;
begin
  Result:=False;
  Result:=Result or OtherModified;
  if(CMForm<>nil) then Result:=Result or CMForm.SourceChanged;
  if(NMForm<>nil) then Result:=Result or NMForm.SourceChanged;
  if(MLForm<>nil) then Result:=Result or MLForm.SourceChanged;
  if(DFForm<>nil) then Result:=Result or DFForm.SourceChanged;
  if(YForm<>nil) then Result:=Result or YForm.SourceChanged;
  // Изменения в отчете игнорируем
  // if(ReportForm<>nil) then Result:=Result or ReportForm.Modified;
end;

procedure TMathPlan.NothingModified;
begin
  OtherModified:=False;
  if(CMForm<>nil) then CMForm.SourceChanged:=False;
  if(NMForm<>nil) then NMForm.SourceChanged:=False;
  if(MLForm<>nil) then MLForm.SourceChanged:=False;
  if(DFForm<>nil) then DFForm.SourceChanged:=False;
  if(YForm<>nil) then YForm.SourceChanged:=False;
end;

function TMathPlan.CreateReport;
var
  S : TMemoryStream;
  p : integer;
begin
  Result:=Fail(acCreateReport);
  if(not CanDo(acCreateReport)) then exit;
  CreateReportForm;
  S:=TMemoryStream.Create;
  S.SetSize(64 * 1024);
  Report(TStream(S));
  fPrint(TStream(S),#13);
  p:=S.Position;
  S.Seek(0,soFromBeginning);
  S.SetSize(p);
  ReportForm.Edit.PlainText:=True;
  ReportForm.Edit.Lines.LoadFromStream(S);
  ReportForm.Edit.PlainText:=False;
  ReportForm.Edit.Modified:=False;
  S.Destroy;
  Result:=Success(acCreateReport);
  MainForm.StateChanged;
end;

// False - НЕЛЬЗЯ выходить, иначе True

function TMathPlan.Close;
var
  Resp:integer;
begin
  Fail(acClose);         // Априори все плохо
  Result:=True;          // Но выходить можно.

  // if(not CanDo(acClose)) then return False ???

  // Были ли данные плана модифицированы - ? Если да, то:
  // спрашиваем;
  // если можем сохранить все целиком - сохраняем;
  // если не можем - экспортируем измененные данные.
  if(Modified) then
  begin
    // Если можем сохранить план как единое целое, то спрашиваем
    // пользователя, и если он ответил "Да" то сохраняем и выходим.
    if(CanDo(acSave) or CanDo(acSaveAs)) then
    begin
      Resp:=AskYNC('Сохранить изменения в "'+SharedData.StrFileName+'"- ?');
      case Resp of
        idYes: // "Да" - сохраняем и выходим.
        begin
          if(CanDo(acSave)) then
            Result:=Save
          else
            Result:=SaveAs;
        end;
        idNo:  Result:=True;     // "Нет" - не сохраняем но выходим
        idCancel: Result:=False; // "Отмена" - не сохраняем и не выходим
      end;
    end
    else // if(CanDo(acSave) or CanDo(acSaveAs))
    begin
      // Не можем сохранить как одно целое - сохраняем по частям
      // Может случиться, что изменения произведены с полями, которые
      // нельзя экспортировать.
      if(OtherModified and (dsYMatrix=dsUndeterminate)) then
      begin
        Resp:=AskYN(
          'В текущем состоянии сохранение плана как целого невозможно -'+
          ' не определена матрица откликов - и Вами изменены данные, '+
          'которые не могут быть экспортированы. Завершить работу с '+
          'текущими данными ?'+#13+
          'Если Вы ответите "Да", то часть данные будут потеряны.');
        // Нет - return False:
        if(Resp=idNo) then
        begin
          Result:=False;
          exit;
        end;
        // Да - продолжаем
      end;
      // Кодовая матрица
      if((dsMainMatrix<>dsUndeterminate)and(CMForm.SourceChanged)) then
      begin
        Resp:=AskYNC('Кодовая матрица изменена. Сохранить изменения - ?');
        case Resp of
          idYes:    Result:=ExportMainMatrix;
          idNo:     Result:=True;
          idCancel: Result:=False;
        end;
        if(not Result) then exit;
      end;
      // Натуральная матрица
      if((dsNaturalMatrix<>dsUndeterminate)and(NMForm.SourceChanged)) then
      begin
        Resp:=AskYNC('Матрица плана изменена. Сохранить изменения - ?');
        case Resp of
          idYes:    Result:=ExportNaturalMatrix;
          idNo:     Result:=True;
          idCancel: Result:=False;
        end;
        if(not Result) then exit;
      end;
      // Основные уровни
      if((dsMLVector<>dsUndeterminate)and(MLForm.SourceChanged)) then
      begin
        Resp:=AskYNC('Значения основных уровней изменены.'+#13+
            'Сохранить изменения - ?');
        case Resp of
          idYes:    Result:=ExportMLVector;
          idNo:     Result:=True;
          idCancel: Result:=False;
        end;
        if(not Result) then exit;
      end;
      // Интервалы варьирования
      if((dsDFVector<>dsUndeterminate)and(DFForm.SourceChanged)) then
      begin
        Resp:=AskYNC('Значения интервалов варьирования изменены.'+#13+
          'Сохранить изменения - ?');
        case Resp of
          idYes:    Result:=ExportDFVector;
          idNo:     Result:=True;
          idCancel: Result:=False;
        end;
        if(not Result) then exit;
      end;
      // Отклики
      if((dsYMatrix<>dsUndeterminate)and(YForm.SourceChanged)) then
      begin
        Resp:=AskYNC('Матрица откликов изменена. Сохранить изменения - ?');
        case Resp of
          idYes:    Result:=ExportYMatrix;
          idNo:     Result:=True;
          idCancel: Result:=False;
        end;
      end;
    end;
    if(not Result) then exit;
  end;
  // Был ли отчет изменен - ?
  if(CanDo(acExportReport) and ReportForm.Edit.Modified and
    AppSetup.GeneralFlags.AskIfReportChanged) then
  begin
    Result:=True;
    Resp:=AskYNC('Содержимое отчета изменено. Сохранить изменения - ?');
    case Resp of
      idYes:  Result:=ReportForm.Save;
      idNo:   Result:=True;
      idCancel: Result:=False;
    end;
    if(not Result) then exit;
  end;
  ReleaseAll;
  CleanFields;
  DestroyForms;
  if(GetGServer<>nil) then
     GetGServer.Reset;
  if(CheckPointDlg.Visible) then
    CheckPointDlg.Close;
  Result:=Success(acClose);
  MainForm.StateChanged;
end;

function TMathPlan.SetKNames:Boolean;
begin
  Result:=Fail(acKNames);
  if(not CanDo(acKNames)) then exit;
  KNamesForm.RealKNames:=@KNames;
  if(KNamesForm.ShowModal=mrOk) then
  begin
    Result:=Success(acKNames);
    OtherModified:=True;
    MainForm.StateChanged;
  end;
end;

function TMathPlan.NewData:Boolean;
begin
  Result:=Fail(acCreateY);
  if(not CanDo(acCreateY)) then exit;
  if(CreateYMatrix) then
  begin
    EditYMatrix(True);
    Result:=Success(acCreateY);
    MainForm.StateChanged;
  end;
end;

procedure TMathPlan.CheckPointDlgClose;
begin
  if(CheckPointDlg.EditForm<>nil) then
  begin
    CheckPointDlg.EditForm.Close;
    CheckPointDlg.EditForm:=nil;
  end;
  if(CheckPointDlg.Result) then
  begin
    CheckPointDlg.CopyCPoint(CPoint);
    CheckPointDlg.CopyCPY(CPY);
    OtherModified:=True;
    Rcp:=CPY.nSize;
    dsCPoint:=dsValid;
    dsCPY:=dsValid;
    dsRcp:=dsValid;
  end;
  Success(acCheckPoint);
end;

function TMathPlan.CheckPoint;
begin
  Result:=Fail(acCheckPoint);
  if(not CanDo(acCheckPoint)) then exit;
  Result:=Success(acCheckPoint);
  if(CheckPointDlg.Visible) then
  begin
    if(CheckPointDlg.WindowState=wsMinimized) then
      CheckPointDlg.WindowState:=wsNormal;
    SetForegroundWindow(CheckPointDlg.Handle);
    exit;
  end;
  if(dsCPoint=dsUndeterminate) then
  begin
    CPoint.Recreate(Degree);
    CPoint.Zero;
  end;
  CheckPointDlg.UseCPoint(CPoint);
  if(dsCPY=dsUndeterminate) then
  begin
    if(dsR<>dsUndeterminate) then
      CPY.Recreate(R)
    else
      CPY.Recreate(4);
    CPY.Zero;
  end;
  CheckPointDlg.UseCPY(CPY);
  CheckPointDlg.OnClose:=CheckPointDlgClose;
  CheckPointDlg.Show;
  Result:=True;
end;

function TMathPlan.ShowAverageData:Boolean;
var
  i,j : integer;
  s : double;
begin
  Result:=Fail(acAverageY);
  if(not CanDo(acAverageY)) then exit;
  if(AVector.nSize<>YMatrix.nRows) then
    AVector.Recreate(YMatrix.nRows);
  for i:=0 to YMatrix.nRows-1 do
  begin
    s:=0;
    for j:=0 to YMatrix.nCols-1 do
      s:=s+YMatrix.Elements^[i]^[j];
    AVector.Elements^[i]:=s/YMatrix.nCols;
  end;
  dsAVector:=dsValid;
  for i:=0 to AVector.nSize-1 do
  begin
    AverageDialog.SGAverage.Cells[i,0]:=IntToStr(i+1);
    AverageDialog.SGAverage.Cells[i,1]:=FloatToStrF(AVector.Elements^[i],
      ffGeneral,15,5);
  end;
  AverageDialog.ShowModal;
  Success(acAverageY);
  MainForm.StateChanged;
end;

function TMathPlan.FullAnalysis:Boolean;
begin
  Result:=Fail(acAnalysis);
  if(not CanDo(acAnalysis)) then exit;
  if(CohrenAnalysis) then
    if(StudentAnalysis) then
      if(FisherAnalysis) then
        Result:=Success(acAnalysis);
  MainForm.StateChanged;
end;

function TMathPlan.FindExtremum:Boolean;
var
  i : integer;
  mt : TMethodType;
  dat : integer;
  RealIndex : integer;
begin
  Result:=Fail(acExtremum);
  if(not CanDo(acExtremum)) then exit;
  ExtrDialog.SetFactors(@KNames);
  ExtrDialog.CanUseOld:=dsMainPoint<>dsUndeterminate;
  if(ExtrDialog.ShowModal<>mrOk) then exit;
  mt:=mtRandom;
  dat:=ExtrDialog.NumSteps;
  RealIndex:=ExtrDialog.CBMethod.Perform(CB_GETITEMDATA,
    ExtrDialog.CBMethod.ItemIndex,0);
  case RealIndex of
    0:
    begin
      mt:=mtScan;
      // Дополнительные данные: количество шагов разбиения
      dat:=ExtrDialog.NumSteps;
    end;
    1:
    begin
      mt:=mtRandom;
      // Дополнительные данные: количество приближений
      dat:=ExtrDialog.NumApps;
    end;
    2:
    begin
      mt:=mtHaussZeidel;
      // Дополнительные данные: количество шагов
      dat:=ExtrDialog.NumSteps;
    end;
    else Assert(False);
  end;
  if(ExtrDialog.CBMinMax.ItemIndex=0) then
    FindMin(ExtrDialog.StartValues,ExtrDialog.EndValues,dat,
      ExtrDialog.CBUseOld.Checked,mt)
  else
    FindMax(ExtrDialog.StartValues,ExtrDialog.EndValues,dat,
      ExtrDialog.CBUseOld.Checked,mt);
  if(dsMainPoint<>dsValid) then exit;
  for i:=0 to MainPoint.nSize-1 do
  begin
    ExDialog.SGAverage.Cells[i,0]:=IntToStr(i+1);
    ExDialog.SGAverage.Cells[i,1]:=FloatToStrF(MainPoint.Elements^[i],
      ffGeneral,15,5);
  end;
  ExDialog.EValue.Text:=FloatToStrF(GetYC(MainPoint),ffGeneral,15,5);
  ExDialog.ShowModal;
  Result:=Success(acExtremum);
  MainForm.StateChanged;
end;

function TMathPlan.Translate:Boolean;
begin
  Result:=Fail(acTranslate);
  if(not CanDo(acTranslate)) then exit;
  TranslateDialog.AttachMLVector(@MLVector);
  TranslateDialog.AttachDFVector(@DFVector);
  if(dsBVector<>dsUndeterminate) then
    TranslateDialog.AttachCountMethod(GetYC)
  else
    TranslateDialog.AttachCountMethod(nil);
  TranslateDialog.ShowModal;
  Success(acTranslate);
  MainForm.StateChanged;
end;

function TMathPlan.NewGraph:Boolean;
var
  i : integer;
  Info : TFactorInfo;
  Delta: double;
  Levels : TVector;
  GNames : TSourceNames;
  NatX : double;
begin
  Result:=Fail(acNewGraph);
  if(not CanDo(acNewGraph)) then exit;
  Assert(GetGServer<>nil);
  Levels.Create(0);
  if(not RunExportDiagramWizard(Info,Levels)) then exit;
  Delta:=(Info.Stop-Info.Start)/(Info.Points-1);
  GetGServer^.BeginGraph(Info.Points,-1);
  if(AppSetup.TeeFlags.NaturalX) then
  begin
    for i:=1 to Info.Points do
    begin
      Levels.Elements^[Info.Number]:=Info.Start;
      NatX:=Info.Start*DFVector.Elements^[Info.Number]+
        MLVector.Elements^[Info.Number];
      GetGServer^.Vertex2D(NatX,GetYC(Levels));
      Info.Start:=Info.Start+Delta;
    end;
  end
  else
  begin
    for i:=1 to Info.Points do
    begin
      Levels.Elements^[Info.Number]:=Info.Start;
      GetGServer^.Vertex2D(Info.Start,GetYC(Levels));
      Info.Start:=Info.Start+Delta;
    end;
  end;
  if(GetGServer^.CanDo(acSetNames)) then
  begin
    GNames.FirstName:=PChar(KNames[Info.Number]);
    GNames.SecondName:=PChar('');
    GNames.ResponseName:=PChar(NameY);
    GetGServer^.SetNames(@GNames,nil);
  end;
  GetGServer^.EndGraph(Info.Points,-1);
  // 5.08.99
  if(AppSetup.TeeFlags.Points) then
  begin
    Assert(dsAVector<>dsUndeterminate);
    if((Degree<2)or(AskYN('Текущий план построен более чем для одного фактора. Корректно разместить экспериментальные точки невозможно (будет выполнено ортогональное проецирование их на плоскость графика). Продолжить - ?')=idYes)) then
    begin
      GetGServer^.BeginPoints2D(AVector.nSize);
      if(AppSetup.TeeFlags.NaturalX) then
      begin
        for i:=0 to AVector.nSize-1 do
          GetGServer^.Vertex2D(NaturalMatrix.GetElement(i,Info.Number),
            AVector.GetElement(i));
      end
      else
      begin
        for i:=0 to AVector.nSize-1 do
          GetGServer^.Vertex2D(MainMatrix.GetElement(i,Info.Number),
            AVector.GetElement(i));
      end;
      GetGServer^.EndPoints2D(AVector.nSize);
    end;
  end;
  Levels.Destroy;
  GetGServer^.Show(True);
  Result:=Success(acNewGraph);
  MainForm.StateChanged;
end;

function TMathPlan.NewSurface:Boolean;
var
  i,j : integer;
  Info1,Info2 : TFactorInfo;
  s2, Delta1,Delta2: double;
  Levels : TVector;
  SNames : TSourceNames;
  NatX,NatY : double;
begin
  Result:=Fail(acNewSurface);
  if(not CanDo(acNewSurface)) then exit;
  Assert(GetGServer<>nil);
  Levels.Create(0);
  if(not RunExportSurfaceWizard(Info1,Info2,Levels)) then exit;
  Delta1:=(Info1.Stop-Info1.Start)/(Info1.Points-1);
  Delta2:=(Info2.Stop-Info2.Start)/(Info2.Points-1);
  // K - число разбиений по второй оси
  // L - число разбиений по первой оси
  GetGServer^.BeginSurface(Info2.Points,Info1.Points);
  if(AppSetup.GLFlags.NaturalXY) then
  begin
    for i:=1 to Info1.Points do
    begin
      s2:=Info2.Start;
      for j:=1 to Info2.Points do
      begin
        Levels.Elements^[Info1.Number]:=Info1.Start;
        Levels.Elements^[Info2.Number]:=s2;
        NatX:=Info1.Start*DFVector.Elements^[Info1.Number]+
          MLVector.Elements^[Info1.Number];
        NatY:=s2*DFVector.Elements^[Info2.Number]+
          MLVector.Elements^[Info2.Number];
        GetGServer^.Vertex3D(NatX,NatY,GetYC(Levels));
        s2:=s2+Delta2;
      end;
      Info1.Start:=Info1.Start+Delta1;
    end;
  end
  else
  begin
    for i:=1 to Info1.Points do
    begin
      s2:=Info2.Start;
      for j:=1 to Info2.Points do
      begin
        Levels.Elements^[Info1.Number]:=Info1.Start;
        Levels.Elements^[Info2.Number]:=s2;
        GetGServer^.Vertex3D(Info1.Start,s2,GetYC(Levels));
        s2:=s2+Delta2;
      end;
      Info1.Start:=Info1.Start+Delta1;
    end;
  end;
  if(GetGServer^.CanDo(acSetNames)) then
  begin
    SNames.FirstName:=PChar(KNames[Info1.Number]);
    SNames.SecondName:=PChar(KNames[Info2.Number]);
    SNames.ResponseName:=PChar(NameY);
    GetGServer^.SetNames(nil,@SNames);
  end;
  GetGServer^.EndSurface(Info2.Points,Info1.Points);
  // 5.08.99
  if(AppSetup.GLFlags.Points) then
  begin
    if((Degree<3)or(AskYN('Текущий план построен более чем для двух факторов. Корректно разместить экспериментальные точки невозможно. Продолжить - ?')=idYes)) then
    begin
      Assert(dsAVector<>dsUndeterminate);
      GetGServer^.BeginPoints3D(AVector.nSize);
      if(AppSetup.GLFlags.NaturalXY) then
      begin
        for i:=0 to AVector.nSize-1 do
          GetGServer^.Vertex3D(NaturalMatrix.GetElement(i,Info1.Number),
            NaturalMatrix.GetElement(i,Info2.Number),
            AVector.GetElement(i));
      end
      else
      begin
        for i:=0 to AVector.nSize-1 do
          GetGServer^.Vertex3D(MainMatrix.GetElement(i,Info1.Number),
            MainMatrix.GetElement(i,Info2.Number),
            AVector.GetElement(i));
      end;
      GetGServer^.EndPoints3D(AVector.nSize);
    end;
  end;
  Levels.Destroy;
  GetGServer^.Show(True);
  Result:=Success(acNewSurface);
  MainForm.StateChanged;
end;

function TMathPlan.ExportGraph;
var
  i : integer;
  Info : TFactorInfo;
  Delta: double;
  Levels : TVector;
  S : TFileStream;
  NatX : double;
begin
  Result:=Fail(acExportGraph);
  if(not CanDo(acExportGraph)) then exit;
  SaveDialog.Title:='Экспорт графика';
  SaveDialog.Filter:=StrTXTFilter;
  SaveDialog.FileName:='';
  if(not SaveExec) then exit;
  Levels.Create(0);
  if(not RunExportDiagramWizard(Info,Levels)) then exit;
  Delta:=(Info.Stop-Info.Start)/(Info.Points-1);
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    if(AppSetup.GeneralFlags.ExportAllCoords) then
    begin
      for i:=1 to Info.Points do
      begin
        Levels.Elements^[Info.Number]:=Info.Start;
        if(AppSetup.TeeFlags.NaturalX) then
          NatX:=Info.Start*DFVector.Elements^[Info.Number]+
            MLVector.Elements^[Info.Number]
        else
          NatX:=Info.Start;
        fPrint(TStream(S),FloatToStr(NatX)+#9+
          FloatToStr(GetYC(Levels))+#13);
        Info.Start:=Info.Start+Delta;
      end;
    end
    else
    begin
      for i:=1 to Info.Points do
      begin
        Levels.Elements^[Info.Number]:=Info.Start;
        fPrint(TStream(S),FloatToStr(GetYC(Levels))+#13);
        Info.Start:=Info.Start+Delta;
      end;
    end;
    S.Destroy;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      Levels.Destroy;
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      Levels.Destroy;
      exit;
    end;
  end;
  Levels.Destroy;
  Result:=Success(acExportGraph);
  MainForm.StateChanged;
end;

function FloatToStrDot(x : Extended):string;
var
  s : string;
  i, len : integer;
begin
  s := FloatToStr(x);
  len := Length(s);
  for i := 1 to len do
    if s[i] = ',' then s[i] := '.';
  Result := s;
end;

function TMathPlan.ExportSurface;
var
  i,j : integer;
  Info1,Info2 : TFactorInfo;
  s1, s2, Delta1,Delta2: double;
  Levels : TVector;
  S : TFileStream;
  NatX, NatY : double;
  Z, Zmin, Zmax : double;
begin
  Result:=Fail(acExportSurface);
  if(not CanDo(acExportSurface)) then exit;
  if(AppSetup.GeneralFlags.ExportAllCoords) then
  begin
    SaveDialog.Title:='Экспорт: все координаты';
    SaveDialog.Filter := StrTXTFilter;
  end
  else
  begin
    SaveDialog.Title:='Экспорт в формат GRD (WinSurf)';
    SaveDialog.Filter := StrGrdFilter;
  end;
  SaveDialog.FileName:='';
  if(not SaveExec) then exit;
  Levels.Create(0);
  if(not RunExportSurfaceWizard(Info1,Info2,Levels)) then exit;
  Delta1:=(Info1.Stop-Info1.Start)/(Info1.Points-1);
  Delta2:=(Info2.Stop-Info2.Start)/(Info2.Points-1);
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    if(AppSetup.GeneralFlags.ExportAllCoords) then
    begin
      for i:=1 to Info1.Points do
      begin
        s2:=Info2.Start;
        for j:=1 to Info2.Points do
        begin
          Levels.Elements^[Info1.Number]:=Info1.Start;
          Levels.Elements^[Info2.Number]:=s2;
          if(AppSetup.GLFlags.NaturalXY) then
          begin
            NatX:=Info1.Start*DFVector.Elements^[Info1.Number]+
              MLVector.Elements^[Info1.Number];
            NatY:=s2*DFVector.Elements^[Info2.Number]+
              MLVector.Elements^[Info2.Number];
          end
          else
          begin
            NatX:=Info1.Start;
            NatY:=s2;
          end;
          fPrint(TStream(S),FloatToStr(NatX)+#9+FloatToStr(NatY)+#9+
            FloatToStr(GetYC(Levels))+#13);
          s2:=s2+Delta2;
        end;
        Info1.Start:=Info1.Start+Delta1;
      end;
    end
    else (* 10.sep.2008: !ExportAllCoords: export as DSAA *)
    begin
      (* pass1: find bounds *)
      s1 := Info1.Start;
      Levels.Elements^[Info1.Number] := Info1.Start;
      Levels.Elements^[Info2.Number] := Info2.Start;
      Zmin := GetYC(Levels);
      Zmax := Zmin;
      for i:=1 to Info1.Points do
      begin
        s2 := Info2.Start;
        for j := 1 to Info2.Points do
        begin
          Levels.Elements^[Info1.Number] := s1;
          Levels.Elements^[Info2.Number] := s2;
          Z := GetYC(Levels);
          if Zmin > Z then
          	Zmin := Z;
          if Zmax < Z then
          	Zmax := Z;
          s2 := s2 + Delta2;
        end;
        s1 := s1 + Delta1;
      end;
      (* pass 2 *)
      fPrint(TStream(S),'DSAA'+#13);
      fPrint(TStream(S),IntToStr(Info1.Points)+#9+IntToStr(Info2.Points)+#13);
      fPrint(TStream(S),
      	FloatToStrDot(Info1.Start*DFVector.Elements^[Info1.Number]+MLVector.Elements^[Info1.Number])+#9+
      	FloatToStrDot(Info1.Stop*DFVector.Elements^[Info1.Number]+MLVector.Elements^[Info1.Number])+#13);
      fPrint(TStream(S),
      	FloatToStrDot(Info2.Start*DFVector.Elements^[Info2.Number]+MLVector.Elements^[Info2.Number])+#9+
      	FloatToStrDot(Info2.Stop*DFVector.Elements^[Info2.Number]+MLVector.Elements^[Info2.Number])+#13);
      fPrint(TStream(S),FloatToStrDot(Zmin)+#9+FloatToStrDot(Zmax)+#13);

      s2 := Info2.Start;
      for j:=1 to Info2.Points do
      begin
        s1 := Info1.Start;
        for i:=1 to Info1.Points do
        begin
          Levels.Elements^[Info1.Number] := s1;
          Levels.Elements^[Info2.Number] := s2;
          fPrint(TStream(S),FloatToStrDot(GetYC(Levels))+#9);
          s1 := s1 + Delta1;
        end;
        s2 := s2 + Delta2;
        fPrint(TStream(S),#13);
      end;

    end;
    S.Destroy;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'". Извините.');
      Levels.Destroy;
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'". Извините.');
      S.Destroy;
      Levels.Destroy;
      exit;
    end;
  end;
  Levels.Destroy;
  Result:=Success(acExportSurface);
  MainForm.StateChanged;
end;

function TMathPlan.ShowMainMatrix:Boolean;
begin
  Result:=CanDo(acShowCode);
  if(not Result) then exit;
  EditCodeMatrix(True);
end;

function TMathPlan.HideMainMatrix:Boolean;
begin
  Result:=CanDo(acHideCode);
  if(not Result) then exit;
  EditCodeMatrix(False);
  MainForm.StateChanged;
end;

function TMathPlan.ShowNaturalMatrix:Boolean;
begin
  Result:=CanDo(acShowNatural);
  if(not Result) then exit;
  EditNaturalMatrix(True);
  MainForm.StateChanged;
end;

function TMathPlan.HideNaturalMatrix:Boolean;
begin
  Result:=CanDo(acHideNatural);
  if(not Result) then exit;
  EditNaturalMatrix(False);
  MainForm.StateChanged;
end;

function TMathPlan.ShowMLVector:Boolean;
begin
  Result:=CanDo(acShowML);
  if(not Result) then exit;
  EditMLVector(True);
  MainForm.StateChanged;
end;

function TMathPlan.HideMLVector:Boolean;
begin
  Result:=CanDo(acHideML);
  if(not Result) then exit;
  EditMLVector(False);
  MainForm.StateChanged;
end;

function TMathPlan.ShowDFVector:Boolean;
begin
  Result:=CanDo(acShowDF);
  if(not Result) then exit;
  EditDFVector(True);
  MainForm.StateChanged;
end;

function TMathPlan.HideDFVector:Boolean;
begin
  Result:=CanDo(acHideDF);
  if(not Result) then exit;
  EditDFVector(False);
  MainForm.StateChanged;
end;

function TMathPlan.ShowYMatrix:Boolean;
begin
  Result:=CanDo(acShowY);
  if(not Result) then exit;
  EditYMatrix(True);
  MainForm.StateChanged;
end;

function TMathPlan.HideYMatrix:Boolean;
begin
  Result:=CanDo(acHideY);
  if(not Result) then exit;
  EditYMatrix(False);
  MainForm.StateChanged;
end;

function TMathPlan.ShowReport:Boolean;
begin
  Result:=False;
  if(not CanDo(acShowReport)) then exit;
  CreateReportForm;
  ReportForm.BringToFront;
  if(ReportForm.WindowState=wsMinimized) then
    ReportForm.WindowState:=wsNormal;
  MainForm.StateChanged;
end;

function TMathPlan.HideReport:Boolean;
begin
  Result:=False;
  if(not CanDo(acHideReport)) then exit;
  if(ReportForm.WindowState<>wsMinimized) then
    ReportForm.WindowState:=wsMinimized;
  MainForm.StateChanged;
end;

function TMathPlan.MinimizeForms;
var
  i : integer;
  procedure FillFormList(Lst:TStrings);
  begin
    Lst.Clear;
    if(CanDo(acHideCode)) then Lst.Add(FormNames[0]);
    if(CanDo(acHideNatural)) then Lst.Add(FormNames[1]);
    if(CanDo(acHideML)) then Lst.Add(FormNames[2]);
    if(CanDo(acHideDF)) then Lst.Add(FormNames[3]);
    if(CanDo(acHideY)) then Lst.Add(FormNames[4]);
    if(CanDo(acHideReport)) then Lst.Add(FormNames[5]);
    // Диаграмма
    if((GetGServer<>nil)and(GetGServer^.CanDo(acHide))) then
      Lst.Add(FormNames[6]);
  end;
begin
  Result:=Fail(acMinimizeForms);
  if(not CanDo(acMinimizeForms)) then exit;
  FillFormList(MinimizeDialog.LBWindows.Items);
  for i:=0 to MinimizeDialog.LBWindows.Items.Count-1 do
    MinimizeDialog.LBWindows.Checked[i]:=True;
  if(MinimizeDialog.CBShowAlways.Checked) then
  begin
    MinimizeDialog.ShowModal;
    if(MinimizeDialog.ModalResult<>mrOk) then exit;
    AppSetup.GeneralFlags.ShowHideList:=
      MinimizeDialog.CBShowAlways.Checked;
  end;
  for i:=0 to MinimizeDialog.LBWindows.Items.Count-1 do
  begin
    if(MinimizeDialog.LBWindows.Checked[i]) then
      SetFormEditing(MinimizeDialog.LBWindows.Items[i],False);
  end;
  Result:=Success(acMinimizeForms);
  MainForm.StateChanged;
end;

function TMathPlan.ShowForms;
var
  i : integer;
  procedure FillFormList(Lst:TStrings);
  begin
    Lst.Clear;
    Lst.Clear;
    if(CanDo(acShowCode)) then Lst.Add(FormNames[0]);
    if(CanDo(acShowNatural)) then Lst.Add(FormNames[1]);
    if(CanDo(acShowML)) then Lst.Add(FormNames[2]);
    if(CanDo(acShowDF)) then Lst.Add(FormNames[3]);
    if(CanDo(acShowY)) then Lst.Add(FormNames[4]);
    if(CanDo(acShowReport)) then Lst.Add(FormNames[5]);
    // Диаграмма
    if((GetGServer<>nil)and(GetGServer^.CanDo(acShow))) then
      Lst.Add(FormNames[6]);
  end;
begin
  Result:=Fail(acShowForms);
  if(not CanDo(acShowForms)) then exit;
  FillFormList(ShowDialog.LBWindows.Items);
  for i:=0 to ShowDialog.LBWindows.Items.Count-1 do
    ShowDialog.LBWindows.Checked[i]:=True;
  if(ShowDialog.CBShowAlways.Checked) then
  begin
    ShowDialog.ShowModal;
    if(ShowDialog.ModalResult<>mrOk) then exit;
    AppSetup.GeneralFlags.ShowShowList:=ShowDialog.CBShowAlways.Checked;
  end;
  for i:=0 to ShowDialog.LBWindows.Items.Count-1 do
  begin
    if(ShowDialog.LBWindows.Checked[i]) then
      SetFormEditing(ShowDialog.LBWindows.Items[i],True);
  end;
  Result:=Success(acShowForms);
  MainForm.StateChanged;
end;

function TMathPlan.ArrangeForms;
  procedure FillFormList(Lst:TStrings);
  begin
    Lst.Clear;
    if(CanDo(acHideCode)) then Lst.Add(FormNames[0]);
    if(CanDo(acHideNatural)) then Lst.Add(FormNames[1]);
    if(CanDo(acHideML)) then Lst.Add(FormNames[2]);
    if(CanDo(acHideDF)) then Lst.Add(FormNames[3]);
    if(CanDo(acHideY)) then Lst.Add(FormNames[4]);
    if(CanDo(acHideReport)) then Lst.Add(FormNames[5]);
    // Диаграмма
    if((GetGServer<>nil)and(GetGServer^.CanDo(acArrange))) then
      Lst.Add(FormNames[6]);
  end;
var
  i : integer;
  Top2, Top3 : integer;
  SplitX,Split1,Split2,Split3 : integer;
  w,h : integer;
  CMVisible : Boolean;
  NMVisible : Boolean;
  MLVisible : Boolean;
  DFVisible : Boolean;
  YVisible  : Boolean;
  UpVisible : Boolean;
  MiddleVisible : Boolean;
  DownVisible : Boolean;
  DiagramVisible : Boolean;
  ReportVisible : Boolean;
  NumberVisible : integer;
begin
  Result := Fail(acArrangeForms);
  if not CanDo(acArrangeForms) then
    exit;
  FillFormList(ArrangeDialog.LBWindows.Items);
  for i:=0 to ArrangeDialog.LBWindows.Items.Count-1 do
    ArrangeDialog.LBWindows.Checked[i]:=True;
  if ArrangeDialog.CBShowAlways.Checked then
  begin
    ArrangeDialog.ShowModal;
    if ArrangeDialog.ModalResult<>mrOk then
      exit;
  end;
  AppSetup.GeneralFlags.ShowArrangeList:=
    ArrangeDialog.CBShowAlways.Checked;
  CMVisible:=False;
  NMVisible:=False;
  MLVisible:=False;
  DFVisible:=False;
  YVisible:=False;
  DiagramVisible:=False;
  ReportVisible:=False;
  with ArrangeDialog.LBWindows do
  begin
    for i:=0 to Items.Count-1 do
    begin
      if(Items[i]=FormNames[0]) then
        CMVisible:=Checked[i];
      if(Items[i]=FormNames[1]) then
        NMVisible:=Checked[i];
      if(Items[i]=FormNames[2]) then
        MLVisible:=Checked[i];
      if(Items[i]=FormNames[3]) then
        DFVisible:=Checked[i];
      if(Items[i]=FormNames[4]) then
        YVisible:=Checked[i];
      if(Items[i]=FormNames[5]) then
        ReportVisible:=Checked[i];
      if(Items[i]=FormNames[6]) then
        DiagramVisible:=Checked[i];
    end;
  end;
  // Начинаем упорядочивание. На первом этапе область разбивается на три
  // горизонтальные полосы: первая полоса для матриц плана, вторая для
  // основных уровней и интервалов варьирования, третья - для откликов и
  // отчета. С диграммой сложнее: серверу могут принадлежать несколько окон
  // с разными графиками. Для сервера математики они трактуются как одно
  // окно; соответственно формируется запрос.
  w:=Rc.Right-Rc.Left;
  h:=Rc.Bottom-Rc.Top;
  // Выясняем необходимость наличия каждой из полос. Упорядочивать будем
  // только те окна, которые видны на экране (и не свернуты).
  // Первая полоса:
  if(CMVisible or NMVisible) then
    UpVisible:=True
  else
    UpVisible:=False;
  // Вторая полоса:
  if(MLVisible or DFVisible) then
    MiddleVisible:=True
  else
    MiddleVisible:=False;
  // Третья полоса:
  if(YVisible or ReportVisible) then
    DownVisible:=True
  else
    DownVisible:=False;
  // Выясняем необходимость наличия каждой из полос. Упорядочивать будем
  // только те окна, которые видны на экране (и не свернуты).
  // Возможные случаи:
  //  - видны все три: 35/25/40
  //  - видны первая и вторая (третья не видна): 70/30
  //  - видны первая и третья (вторая не видна): 40/60
  //  - видны вторая и третья (первая не видна): 30/70
  //  - видна только первая: 100
  //  - видна только вторая: 100
  //  - видна только третья: 100
  //  - не видно не одной
  NumberVisible:=0;
  if UpVisible then
    Inc(NumberVisible);
  if MiddleVisible then
    Inc(NumberVisible);
  if DownVisible then
    Inc(NumberVisible);
  Top2 := -1;
  Top3 := -1;
  case NumberVisible of
    0: // Не видно не одной
    begin
      exit;
    end;
    1: // Видна ровно одна
    begin
      if(UpVisible) then
      begin
        // Видна первая
        Top2:=Rc.Bottom;
        Top3:=Top2;
      end;
      if(MiddleVisible) then
      begin
        // Видна вторая
        Top2:=Rc.Top;
        Top3:=Rc.Bottom;
      end;
      if(DownVisible) then
      begin
        // Видна третья
        Top2:=Rc.Top;
        Top3:=Top2;
      end;
    end;
    2: // Видны ровно две
    begin
      if(UpVisible and MiddleVisible) then
      begin
        // Видны первая и вторая
        Top2:=Round(Rc.Top+7*h/10);
        Top3:=Rc.Bottom;
      end;
      if(UpVisible and DownVisible) then
      begin
        // Видны первая и третья
        Top2:=Round(Rc.Top+4*h/10);
        Top3:=Top2;
      end;
      if(MiddleVisible and DownVisible) then
      begin
        // Видны вторая и третья
        Top2:=Rc.Top;
        Top3:=Round(Rc.Top+3*h/10);
      end;
    end;
    3: // Видны все
    begin
      Top2:=Round(Rc.top+35*h/100);
      Top3:=Round(Top2+25*h/100);
    end;
  end;
  Assert(Top2<>-1);
  Assert(Top3<>-1);
  // На следующем этапе выясняем, нужна ли внутри каждой полосы граница
  // раздела.
  SplitX:=Round(Rc.Left+w/2);
  Split1:=SplitX;
  Split2:=SplitX;
  Split3:=SplitX;
  // Первая полоса:
  if UpVisible then
  begin
    if(CMVisible and (not NMVisible)) then
      Split1:=Rc.Right // Видна только левая часть
    else
      if((not CMVisible) and NMVisible) then
        Split1:=Rc.Left; // Видна только правая часть
  end;
  // Вторая полоса:
  if MiddleVisible then
  begin
    if(MLVisible and (not DFVisible)) then
      Split2:=Rc.Right // Видна только левая часть
    else
      if((not MLVisible) and DFVisible) then
        Split2:=Rc.Left; // Видна только правая часть
  end;
  // Третья полоса:
  if DownVisible then
  begin
    if(YVisible and (not ReportVisible)) then
      Split3:=Rc.Right // Видна только левая часть
    else
      if((not YVisible) and ReportVisible) then
        Split3:=Rc.Left; // Видна только правая часть
  end;
  // Изменение размеров и z - порядка.
  if ReportVisible then
  begin
    ReportForm.WindowState:=wsNormal;
    ReportForm.Left:=Split3;
    ReportForm.Top:=Top3;
    ReportForm.Width:=Rc.Right-Split3;
    ReportForm.Height:=Rc.bottom-Top3;
    ReportForm.BringToFront;
  end;
  if YVisible then
  begin
    YForm.WindowState:=wsNormal;
    YForm.Left:=Rc.Left;
    YForm.Top:=Top3;
    YForm.Width:=Split3-Rc.Left;
    YForm.Height:=Rc.bottom-Top3;
    YForm.BringToFront;
  end;
  if DFVisible then
  begin
    DFForm.WindowState:=wsNormal;
    DFForm.Left:=Split2;
    DFForm.Top:=Top2;
    DFForm.Width:=Rc.Right-Split2;
    DFForm.Height:=Top3-Top2;
    DFForm.BringToFront;
  end;
  if MLVisible then
  begin
    MLForm.WindowState:=wsNormal;
    MLForm.Left:=Rc.Left;
    MLForm.Top:=Top2;
    MLForm.Width:=Split2-Rc.Left;
    MLForm.Height:=Top3-Top2;
    MLForm.BringToFront;
  end;
  if NMVisible then
  begin
    NMForm.WindowState:=wsNormal;
    NMForm.Left:=Split1;
    NMForm.Top:=Rc.Top;
    NMForm.Width:=Rc.Right-Split1;
    NMForm.Height:=Top2-Rc.Top;
    NMForm.BringToFront;
  end;
  if CMVisible then
  begin
    CMForm.WindowState:=wsNormal;
    CMForm.Left:=Rc.Left;
    CMForm.Top:=Rc.Top;
    CMForm.Width:=Split1-Rc.Left;
    CMForm.Height:=Top2-Rc.Top;
    CMForm.BringToFront;
  end;
  if (GetGServer<>nil) and DiagramVisible then
    GetGServer^.Arrange;
  Result:=Success(acArrangeForms);
end;

// =======================================================================
// Код защищенных методов
// =======================================================================

// -----------------------------------------------------------------------
// StreamSave
// -----------------------------------------------------------------------

function TMathPlan.StreamSave;
var
  mpd : TMathPlanData;
begin       
  mpd.Create;
  Result:=mpd.MakeCopy(Self) and mpd.SaveToStream(Stream);
  mpd.Destroy;
  NothingModified;
end;

function TMathPlan.StreamLoad;
var
  mpd : TMathPlanData;
begin
  mpd.Create;
  Result:=mpd.LoadFromStream(Stream) and mpd.EnliveObject(Self);
  mpd.Destroy;
end;

procedure TMathPlan.FillBNames;
var
  l, m, count : integer;
  val : double;
begin
  if(dsBVector=dsUndeterminate) then exit;
  BNames.Clear;
  dsBNames:=dsUndeterminate;
  if(pType=ptFromWizard) then
  begin
    if(Model<>nil) then
    begin
      if(BVector.nSize<>Model^.AddenCount) then
      begin
        Warning('BVector.nSize<>Model^.AddenCount'+#13+
          'ГЛЮК. Обратитесь к разработчику.');
        exit;
      end;
      for l:=0 to BVector.nSize-1 do
      begin
        val:=Bvector.GetElement(l);
        if Boolean(AddenUsed[l]) then
        begin
          BNames.Add(Format('%s=%5.5g',[Model^.Addens(l)^.StdBName,val]));
        end
        else
        begin
          BNames.Add(Format('%s=%5.5g (статистически не значим, слагаемое модели должно быть исключено)',
            [Model^.Addens(l)^.StdBName, val]));
        end;
      end;
    end
    else
    begin
      for l:=0 to BVector.nSize-1 do
      begin
        BNames.Add(Format('%s=%5.5g',[PAdden(Addens[l])^.StdBName,Bvector.GetElement(l)]));
      end;
    end;
    dsBNames:=dsValid;
    exit;
  end;
  if(not (pType in [ptCCR,ptCCO,ptFFE])) then
  begin
    Warning('FillNames: неверный тип плана.'+#13+
      'Обратитесь в разработчику.');
    exit;
  end;
  for l:=0 to Degree do
  begin
    BNames.Add(Format('B%d=%5.5g',[l,Bvector.GetElement(l)]))
  end;
  if(pType=ptFFE) then
  begin
    dsBNames:=dsValid;
    exit;
  end;
  // pType == CCR or CCR
  count:=Degree+1;
  for l:=0 to Degree-2 do
    for m:=l+1 to Degree-1 do
    begin
      val:=BVector.GetElement(count);
      BNames.Add(Format('B%d%d=%5.5g',[l+1,m+1,val]));
      Inc(count);
    end;
  for l:=1 to Degree do
  begin
    val:=BVector.GetElement(count);
    BNames.Add(Format('B%d%d=%5.5g',[l,l,val]));
    Inc(count);
  end;
  dsBNames:=dsValid;
end;

function TMathPlan.GetAlpha;
begin
  Result:=0;
  if(pT=ptCCR) then
  begin
    // alpha := 2 ^ (k/4) - ротатабельный план на гиперсфере
    Result:=sqrt(sqrt(1 shl Degree));
    exit;
  end;
  if(pT=ptCCO) then
  begin
    // alpha := (( (2^k * N)^.5 - 2^k)/2)^.5 - ортогональный план
    Result:=sqrt((sqrt((1 shl Degree)*N) - (1 shl Degree) )/2);
  end;
end;

// =======================================================================
// Очистка полей. Выполняется один раз - при создании объекта.
// =======================================================================
procedure TMathPlan.CleanFields;
begin
  dsDegree:=dsUndeterminate;
  dspType:=dsUndeterminate;
  dsN:=dsUndeterminate;
  dsAlpha:=dsUndeterminate;
  dsMainMatrix:=dsUndeterminate;
  MainMatrix.Modified:=False;
  dsMLVector:=dsUndeterminate;
  MLVector.Modified:=False;
  dsDFVector:=dsUndeterminate;
  DFVector.Modified:=False;
  dsNaturalMatrix:=dsUndeterminate;
  NaturalMatrix.Modified:=False;
  dsExpandedMatrix:=dsUndeterminate;
  dsMMatrix:=dsUndeterminate;
  dsCMatrix:=dsUndeterminate;
  dsR:=dsUndeterminate;
  dsYMatrix:=dsUndeterminate;
  YMatrix.Modified:=False;
  dsDV:=dsUndeterminate;
  dsAVector:=dsUndeterminate;
  dsDVector:=dsUndeterminate;
  dsSVector:=dsUndeterminate;
  dsDMax:=dsUndeterminate;
  dsBVector:=dsUndeterminate;
  dsBNames:=dsUndeterminate;
  dsKNames:=dsUndeterminate;
  dsDYVector:=dsUndeterminate;
  dsActualBSize:=dsUndeterminate;
  dsDad:=dsUndeterminate;
  dsCH_R:=dsUndeterminate;
  dsFS_R:=dsUndeterminate;
  dsCH_T:=dsUndeterminate;
  dsST_T:=dsUndeterminate;
  dsFS_T:=dsUndeterminate;
  dsCPoint:=dsUndeterminate;
  CPoint.Modified:=False;
  dsCPY:=dsUndeterminate;
  CPY.Modified:=False;
  dsRcp:=dsUndeterminate;
  dsST_R:=dsUndeterminate;
  dsST_T:=dsUndeterminate;
  dsDfore:=dsUndeterminate;
  dsMainPoint:=dsUndeterminate;
  OtherModified:=False;
  Gen:=nil;
  Model:=nil;
  CurrentFile:='';
end;

// =======================================================================
// Уборка мусора.
// =======================================================================
procedure TMathPlan.ReleaseAll;
begin
  DestroyForms;
  CMatrix.Destroy;
  MMatrix.Destroy;
  MLVector.Destroy;
  DFVector.Destroy;
  YMatrix.Destroy;
  AVector.Destroy;
  DVector.Destroy;
  SVector.Destroy;
  BVector.Destroy;
  DYVector.Destroy;
  MainPoint.Destroy;
  CPoint.Destroy;
  CPY.Destroy;
  dsMainMatrix:=dsUndeterminate;
  dsNaturalMatrix:=dsUndeterminate;
  dsMLVector:=dsUndeterminate;
  dsDFVector:=dsUndeterminate;
  dsYMatrix:=dsUndeterminate;
  dsAVector:=dsUndeterminate;
  dsDVector:=dsUndeterminate;
  dsSVector:=dsUndeterminate;
  dsBVector:=dsUndeterminate;
  dsDYVector:=dsUndeterminate;
  dsMainPoint:=dsUndeterminate;
end;

// =======================================================================
// Конструктор
// =======================================================================
constructor TMathPlan.Create;
begin
  Alpha:=0;
  MainMatrix.Create(0,0);
  CPoint.Create(0);
  CPY.Create(0);
  MLVector.Create(0);
  DFVector.Create(0);
  NaturalMatrix.Create(0,0);
  ExpandedMatrix.Create(0,0);
  MMatrix.Create(0,0);
  CMatrix.Create(0,0);
  R:=0;
  YMatrix.Create(0,0);
  DV:=0;
  AVector.Create(0);
  DVector.Create(0);
  SVector.Create(0);
  DMax:=0;
  BVector.Create(0);
  BNames:=TStringList.Create;
  KNames:=TStringList.Create;
  ActualBSize:=0;
  DYVector.Create(0);
  Dad:=0;
  CH_R:=0;
  FS_R:=0;
  CH_T:=0;
  ST_R:=0;
  ST_T:=0;
  FS_T:=0;
  MainPoint.Create(0);
  Addens:=TList.Create;
  Addens.Clear;
  AddenUsed:=TList.Create;
  AddenUsed.Clear;
  CleanFields;
  NameY:='Отклик системы';
  CMName:='Кодовая матрица.txt';
  NMName:='Матрица планирования.txt';
  MLName:='Основные уровни.txt';
  DFName:='Интервалы варьирования.txt';
  YName:='Отклики.txt';
  FormNames[0]:='Кодовая матрица';
  FormNames[1]:='Матрица планирования';
  FormNames[2]:='Вектор основных уровней';
  FormNames[3]:='Вектор интервалов варьирования';
  FormNames[4]:='Матрица откликов';
  FormNames[5]:='Окно отчета';
  FormNames[6]:='Диаграмма';
  OpenDialog:=TOpenDialog.Create(Application.MainForm);
  SaveDialog:=TSaveDialog.Create(Application.MainForm);
  OpenDialog.Filter:='';
  StrTXTFilter:='Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*';
  StrGRDFilter:='ASCII-файлы сетки для Surfer (*.grd)|*.grd|Все файлы (*.*)|*.*';
  StrMPLFilter:='Файлы планирования (*.mpl)|*.mpl|Все файлы (*.*)|*.*';
  OpenDialog.InitialDir:='.';
  OpenDialog.Options:=[ofHideReadOnly,ofPathMustExist,ofFileMustExist];
  SaveDialog.Filter:='';
  SaveDialog.InitialDir:='.';
  SaveDialog.Options:=[ofOverwritePrompt,ofHideReadOnly];
  OpenResult:=False;
  SaveResult:=False;
  CMForm:=nil;
  NMForm:=nil;
  MLForm:=nil;
  DFForm:=nil;
  YForm:=nil;
  ReportForm:=nil;
  Gen:=nil;
  Model:=nil;
  CreateGraphServer(pIGServer);
  Assert(pIGServer<>nil);
end;

// =======================================================================
// Деструктор
// =======================================================================
destructor TMathPlan.Destroy;
begin
  CleanUp;
  BNames.Free;
  KNames.Free;
  Addens.Free;
  DestroyForms;
  SaveDialog.Free;
  OpenDialog.Free;
  DestroyGraphServer(pIGServer);
  AddenUsed.Free;
  Inherited;
end;

// =======================================================================
// RunWizard
// =======================================================================
function RunWizard : Boolean;
label
  st1,st2,st3,st2a,st3a,st4;
begin
st1:
  StepOneForm.ShowModal;
  case StepOneForm.Result of
    wForward:
    begin
      if(StepOneForm.RBFromGen.Checked) then goto st2;
      if(StepOneForm.RBCustom.Checked) then goto st2a;
      if(StepOneForm.RBOld.Checked) then
      begin
        Result:=True;
        exit;
      end;
      Assert(False);
    end;
    wCancel:
    begin
      Result:=False;
      exit;
    end;
    else Assert(False);
  end;
  Assert(False);
st2:
  StepTwoForm.ShowModal;
  case StepTwoForm.Result of
    wBack:
    begin
      goto st1;
    end;
    wForward:
    begin
      if(StepTwoForm.CBUseDefaultModel.Checked) then
      begin
        Result:=True;
        exit;
      end;
      goto st3;
    end;
    wCancel:
    begin
      Result:=False;
      exit;
    end;
    else Assert(False);
  end;
  Assert(False);
st2a:      // Assert(StepTwoForm.LBModels.ItemIndex
  StepTwoForm_A.ShowModal;
  case StepTwoForm_A.Result of
    wBack:
    begin
      goto st1;
    end;
    wForward:
    begin
      goto st3;
    end;
    wCancel:
    begin
      Result:=False;
      exit;
    end;
    else Assert(False);
  end;
  Assert(False);
st3:
  StepThreeForm.ShowModal;
  case StepThreeForm.Result of
    wBack:
    begin
      if(StepOneForm.RBCustom.Checked) then goto st2a;
      goto st2;
    end;
    wForward:
    begin
      if(StepThreeForm.CBUseDefaultModel.Checked) then
      begin
        Result:=True;
        exit;
      end;
      goto st4;
    end;
    wCancel:
    begin
      Result:=False;
      exit;
    end;
    else Assert(False);
  end;
  Assert(False);
st4:
  StepFourForm.ShowModal;
  case StepFourForm.Result of
    wBack:
    begin
      goto st3;
    end;
    wForward:
    begin
      Result:=True;
      exit;
    end;
    wCancel:
    begin
      Result:=False;
      exit;
    end;
    else Assert(False);
  end;
  Result:=False;
  Assert(False);
end;

procedure TMathPlan.ShowDataForms;
begin
  // 25.01.00 - rearranged
  EditNaturalMatrix(True);
  EditMLVector(True);
  EditDFVector(True);
  EditCodeMatrix(True);
  EditYMatrix(True);
end;

procedure TMathPlan.GenerateDeafultKNames;
var
  i : integer;
begin
  KNames.Clear;
  KNames.Capacity:=Degree;
  for i:=1 to Degree do
    KNames.Add(Format('Фактор № %d',[i]));
  dsKNames:=dsValid;
end;

function TMathPlan.SetDegree(Deg:integer):Boolean;
begin
  if(Deg<1) then
  begin
    dsDegree:=dsUndeterminate;
    __Degree:=-1;
    Result:=False;
    exit;
  end;
  __Degree:=Deg;
  Result:=True;
end;

function TMathPlan.Degree:integer;
begin
  Result:=__Degree;
end;

// =======================================================================
// Создание нового плана
// На выходе
//   - поля допустимости соответствующих данных != dsUndeterminate
// =======================================================================
function TMathPlan.NewPlan;
var
  msg : string;
  _n,k,i : integer;
  ptr : pointer;
  nb : integer;
const
  NoMem : string = 'Недостаточно памяти для создания плана. Извините.';
begin
  Result:=False;
  msg:='Невозможно создать план: '+#13;
  if(not RunWizard) then exit;
  // =====================================================================
  if(not StepOneForm.RBOld.Checked) then
  begin
    // ===================================================================
    // Создать план по данным в формах мастера:
    // Используется ли генератор: форма 1, если нет - то в 2а параметры
    // Используется ли модель генератора: форма 2, если нет -
    //   используется ли стандартная модель: форма 3, если нет -
    //     слагаемые по форме 4.
    Gen:=nil;
    Model:=nil;
    Addens.Clear;
    if(StepOneForm.RBFromGen.Checked) then
    begin
      _n:=ExternalModules.GenByName(
        StepTwoForm.LBPlans.Items[StepTwoForm.LBPlans.ItemIndex]);
      Assert(_n<>-1);
      Gen:=ExternalModules.Gens[_n];
      Assert(Gen<>nil);
      Gen^.RequiredDimensions(_n,k);
      Assert((_n>0)and(k>0));
      SetDegree(k);
      N:=_n;
    end
    else // NOT StepOneForm.RBFromGen.Checked
    begin
      // Генератор не используется
      k:=StepTwoForm_A.KEdit.Value;
      _n:=StepTwoForm_A.NEdit.Value;
      Assert((_n>0)and(k>0));
      SetDegree(k);
      N:=_n;
    end;
    if(Gen<>nil) then
    begin
      if(StepTwoForm.CBUseDefaultModel.Checked) then
      begin
        Model:=Gen^.DefaultModel;
        Assert(Model<>nil);
      end;
    end;
    if(Model=nil) then
    begin
      if(StepThreeForm.CBUseDefaultModel.Checked) then
      begin
        _n:=ExternalModules.ModelByName(StepThreeForm.LBModels.Items[
          StepThreeForm.LBModels.ItemIndex]);
        Assert(_n<>-1);
        Model:=ExternalModules.Models[_n];
        Assert(Model<>nil);
      end;
    end;
    if(Model=nil) then
    begin
      _n:=StepFourForm.Addens.Count;
      Assert((_n>0)and(_n<=Self.N));
      Addens.Clear;
      Addens.Capacity:=_n;
      for k:=0 to _n-1 do
      begin
        ptr:=StepFourForm.Addens[k];
        Assert(ptr<>nil);
        Addens.Add(ptr);
      end;
    end;
    pType:=ptFromWizard;
  end
  else
  begin
    // Создать встроенный план
    NewDialog.ShowModal;
    if(NewDialog.ModalResult<>mrOk) then exit;
    if(NewDialog.Degree<2) then
    begin
      Warning(msg+'План порядка '+IntToStr(Degree)+' не поддерживается.');
      exit;
    end;
    if(not SetDegree(NewDialog.Degree)) then exit;
    pType:=NewDialog.pType;
    if(not (pType in [ptFFE,ptCCO,ptCCR])) then
    begin
      Warning(msg+'Неизвестный тип плана.');
      SetDegree(-1);
      exit;
    end;
    dspType:=dsValid;
    // Данные корректны.
    dsN:=dsValid;
    case pType of
      ptFFE: N:=1 shl Degree;
      ptCCR: N:=(1 shl Degree)+2*Degree+1;
      ptCCO: N:=(1 shl Degree)+2*Degree+1;
    end;
  end;
  // Начинается выделение памяти
  try
    CreateCodeMatrix;
    NaturalMatrix.Recreate(N,Degree);
    MLVector.Recreate(Degree);
    DFVector.Recreate(Degree);
    dsDegree:=dsValid;
    dsN:=dsValid;
    dspType:=dsValid;
    dsMainMatrix:=dsValid;
    dsNaturalMatrix:=dsValid;
    dsMLVector:=dsValid;
    dsDFVector:=dsValid;
    NaturalMatrix.Zero;
    MLVector.Zero;
    // Создаем матрицу откликов
    if(AppSetup.GeneralFlags.AlsoCreateY) then
    begin
      if(not CreateYMatrix) then
      begin
        dsDegree:=dsUndeterminate;
        exit;
      end;
    end;
  except
    on EOutOfMemory do
    begin
      Error(NoMem);
      exit;
    end;
  end;
  for i:=0 to DFVector.nSize-1 do DFVector.Elements^[i]:=1.0;
  // Создаем имена факторов.
  GenerateDeafultKNames;
  // Если план насыщенный - запрашиваем данные для контрольной точки
  if(pType=ptFromWizard) then
  begin
    if(Model=nil) then
      nb:=Addens.Count
    else
      nb:=Model^.AddenCount;
    if((nb=N)and(AppSetup.GeneralFlags.CPForSaturated)) then
    begin
      // 25.01.00
      if((dsR=dsValid)and(R>1)) then
        CheckPoint;
    end;
  end;
  // Показываем на экране соответствующие формы
  ShowDataForms;
  MainForm.SynchronizeMatrices;
  MainForm.StateChanged;
  Result:=True;
end;

// =======================================================================
// Загрузка кодовой матрицы из файла. Если план еще не создан, то он
// создается. Если план уже создан, то загружаемая матрица должна иметь
// тот же размер, что и существующая.
// =======================================================================
function TMathPlan.LoadMainMatrix;
label
  BreakThisLoops;
var
  M       : TStreamableMatrix;
  S       : TFileStream;
  i,j     : integer;
  AlphaR  : double;
  AlphaO  : double;
begin
  Result:=False;
  try
    S := TFileStream.Create(FileName,fmOpenRead);
  except
    on EFOpenError do
    begin
      Error('Ошибка открытия файла "'+FileName+'".'+#13+
        'Файл не существует или используется другим'+#13+
        'приложением. Извините.');
      exit;
    end;
  end;
  if(dsDegree=dsUndeterminate) then
  begin
    dsMainMatrix:=dsUndeterminate;
    MainMatrix.LoadFromStreamAsText(TStream(S));
    if((MainMatrix.nCols=0)or(MainMatrix.nRows=0)) then
    begin
      S.Seek(0,soFromBeginning);
      MainMatrix.LoadFromStream(TStream(S));
    end;
    S.Destroy;
    if((MainMatrix.nCols>0)and(MainMatrix.nRows>0)) then
    begin
      // По кодовой матрице можно восстановить весь план. Для этого
      // достаточно положить количество факторов:=число столбцов,
      // количество опытов:=число строк, и проверить, какой из планов
      // соответствует указанному случаю.
      SetDegree(MainMatrix.nCols);
      N:=MainMatrix.nRows;
      // Если число опытов = 2 ^ число факторов, то сомнений не возникает
      if(N=(1 shl Degree)) then pType:=ptFFE
      else
      if(N=((1 shl Degree)+2*Degree+1)) then
      begin
        // Единственный способ определить вид - искать в матрице требуе-
        // мое звездное плечо
        AlphaO:=GetAlpha(ptCCO);
        AlphaR:=GetAlpha(ptCCR);
        for i:=0 to MainMatrix.nRows-1 do
        begin
          for j:=0 to MainMatrix.nRows-1 do
          begin
            if(abs(MainMatrix.GetElement(i,j)-AlphaR)<AlphaR/100) then
            begin
              pType:=ptCCR;
              dsAlpha:=dsValid;
              Alpha:=AlphaR;
              goto BreakThisLoops;
            end;
            if(abs(MainMatrix.GetElement(i,j)-AlphaO)<AlphaO/100) then
            begin
              pType:=ptCCO;
              dsAlpha:=dsValid;
              Alpha:=AlphaO;
              goto BreakThisLoops;
            end
          end;
        end;
        // Определить не удалось. Может, пользователь знает - ?
        DlgCCOCCR.ShowModal;
        if(DlgCCOCCR.ModalResult<>mrOk) then exit;
        case DlgCCOCCR.RGType.ItemIndex of
          0:
          begin
            pType:=ptCCO;
            Alpha:=AlphaO;
          end;
          1:
          begin
            pType:=ptCCR;
            Alpha:=AlphaR;
          end;
        end;
        if(DlgCCOCCR.CBUseDefaultAlpha.Checked) then
        begin
          M.Create(1 shl MainMatrix.nCols,MainMatrix.nCols);
          MainMatrix.GetSubMat(M,0,0);
          CreateCodeMatrix;
          MainMatrix.SetSubMat(M,0,0);
          M.Destroy;
        end;
        BreakThisLoops:
      end
      else // N != (1 << Degree) + (Degree << 1) + 1
      begin
        Warning('Файл "'+FileName+'" содержит'+#13+
          'описание матрицы неизвестного плана.');
        exit;
      end;
      dsMainMatrix:=dsValid;
      dsDegree:=dsValid;
      dsN:=dsValid;
      dspType:=dsValid;
      MLVector.Recreate(Degree);
      MLVector.Zero;
      dsMLVector:=dsValid;
      DFVector.Recreate(Degree);
      dsDFVector:=dsValid;
      for i:=0 to DFVector.nSize-1 do DFVector.Elements^[i]:=1.0;
      NaturalMatrix.Recreate(N,Degree);
      NaturalMatrix.Zero;
      dsNaturalMatrix:=dsValid;
      SynchronizeMatrices(Self);
      EditCodeMatrix(True);
      EditNaturalMatrix(True);
      EditMLVector(True);
      EditDFVector(True);
      Result:=True;
    end
    else
    begin
      Warning('Файл "'+FileName+'" должен содержать описание матрицы.');
      exit;
    end;
  end
  else // dsDegree != dsUndeterminate
  begin
    M.Create(0,0);
    M.LoadFromStreamAsText(TStream(S));
    if((M.nCols=0)or(M.nRows=0)) then
    begin
      S.Seek(0,soFromBeginning);
      M.LoadFromStream(TStream(S));
    end;
    S.Destroy;
    if((M.nCols<>Degree)or(M.nRows<>N)) then
    begin
      Warning('Файл "'+FileName+'" должен содержать'+#13+
        'описание матрицы из '+IntToStr(N)+
        'строк и '+IntToStr(Degree)+'столбцов.');
      exit;
    end
    else
    begin
      MainMatrix.CopyFrom(M);
      EditCodeMatrix(True);
      Result:=True;
    end;
    M.Destroy;
  end;
end;

// =======================================================================
// Загрузка вектора основных уровней.
// =======================================================================
function TMathPlan.LoadMLVector;
var
  V       : TStreamableVector;
  S       : TFileStream;
begin
  Result:=False;
  if(dsMLVector=dsUndeterminate) then
  begin
    Warning('Невозможно открыть вектор основный уровней'+#13+
      '- его размер неизвестен.');
    exit;
  end;
  V.Create(0);
  try
    S := TFileStream.Create(FileName,fmOpenRead);
  except
    on EFOpenError do
    begin
      Error('Ошибка открытия файла "'+FileName+'".'+#13+
        'Файл не существует или используется другим'+#13+
        'приложением. Извините.');
      exit;
    end;
  end;
  V.LoadFromStreamAsText(TStream(S));
  if(V.nSize=0) then
  begin
    S.Seek(0,soFromBeginning);
    V.LoadFromStream(TStream(S));
  end;
  S.Destroy;
  if(V.nSize<>MLVector.nSize) then
  begin
    Warning('Файл "'+FileName+'" должен содержать описание'+#13+
      'вектора из '+IntToStr(MLVector.nSize)+' элементов.');
    exit;
  end
  else
  begin
    MLVector.CopyFrom(V);
    Result:=True;
    EditMLVector(True);
  end;
end;

// =======================================================================
// Загрузка вектора интервалов варьирования.
// =======================================================================
function TMathPlan.LoadDFVector;
var
  V       : TStreamableVector;
  S       : TFileStream;
begin
  Result:=False;
  if(dsDFVector=dsUndeterminate) then
  begin
    Warning('Невозможно открыть вектор интервалов варьирования:'+#13+
      '- его размер неизвестен');
    exit;
  end;
  V.Create(0);
  try
    S := TFileStream.Create(FileName,fmOpenRead);
  except
    on EFOpenError do
    begin
      Error('Ошибка открытия файла "'+FileName+'".'+#13+
        'Файл не существует или используется другим'+#13+
        'приложением. Извините.');
      exit;
    end;
  end;
  V.LoadFromStreamAsText(TStream(S));
  if(V.nSize=0) then
  begin
    S.Seek(0,soFromBeginning);
    V.LoadFromStream(TStream(S));
  end;
  S.Destroy;
  if(V.nSize<>DFVector.nSize) then
  begin
    Warning('Файл "'+FileName+'" должен содержать описание'+#13+
      'вектора из '+IntToStr(DFVector.nSize)+' элементов.');
    exit;
  end
  else
  begin
    DFVector.CopyFrom(V);
    EditDFVector(True);
    Result:=True;
  end;
end;

// =======================================================================
// Создание матрицы откликов.
// =======================================================================
function TMathPlan.CreateYMatrix;
var
  MsgBeg : string;
  TempMat : TStreamableMatrix;
begin
  Result:=False;
  MsgBeg:='Невозможно начать редактирование откликов:'+#13;
  if(dsN=dsUndeterminate) then
  begin
    Warning(MsgBeg+'неизвестно количество опытов.');
    exit;
  end;
  // Выводим диалог создания/загрузки матрицы наблюдений
  if(dsYMatrix<>dsUndeterminate) then
    YMparams.Caption:='Изменить количество повторений'
  else
    YMparams.Caption:='Новый набор наблюдений';
  YMparams.EY.Text:=NameY;
  YMparams.ShowModal;
  if(YMparams.ModalResult=mrCancel) then exit;
  if(YMparams.EY.Text<>'') then
    NameY:=YMparams.EY.Text
  else
    NameY:='Отклик системы';
  // Требуемое число строк в матрице откликов - число опытов плана
  if(Length(YMparams.ERepeats.Text) in [1..3]) then
    R:=YMparams.ERepeats.Value
  else
    R:=-1;
  dsR:=dsValid;
  if(dsYMatrix=dsUndeterminate) then
  begin
    YMatrix.ReCreate(N,R);
    YMatrix.Zero;
    dsYMatrix:=dsValid;
  end
  else
  begin
    CreateYForm;
    if(YMatrix.nCols > R) then
    begin
      // Матрица "обрезается"
      TempMat.Create(N,R);
      YMatrix.GetSubMat(TempMat,0,0);
      YMatrix.ReCreate(N,R);
      YMatrix.CopyFrom(TempMat);
    end
    else
    begin
      // Матрица "расширяется"
      TempMat.Create(YMatrix.nRows,YMatrix.nCols);
      TempMat.CopyFrom(YMatrix);
      YMatrix.ReCreate(N,R);
      YMatrix.Zero;
      YMatrix.SetSubMat(TempMat,0,0);
    end;
    YForm.SourceChanged:=True;
  end;
  EditYMatrix(True);
  Result:=True;
end;

function TMathPlan.LoadYMatrix;
var
  S      : TFileStream;
  M      : TStreamableMatrix;
begin
  Result:=False;
  M.Create(0,0);
  try
    S := TFileStream.Create(FileName,fmOpenRead);
  except
    on EFOpenError do
    begin
      Error('Ошибка открытия файла "'+FileName+'".'+#13+
        'Файл не существует или используется другим'+#13+
        'приложением. Извините.');
      exit;
    end;
  end;
  if(not M.LoadFromStreamAsText(TStream(S))) then
  begin
    S.Seek(0,soFromBeginning);
    M.LoadFromStream(TStream(S));
  end;
  S.Destroy;
  if((M.nRows<>N)or(M.nCols<2)) then
  begin
    Warning('Файл "'+FileName+'" должен содержать'+#13+
      'описание матрицы с двумя или более столбцами'+#13+
      'и числом строк, равным '+IntToStr(N));
    exit;
  end;
  R:=M.nCols;
  dsR:=dsValid;
  YMatrix.Create(N,R);
  YMatrix.CopyFrom(M);
  dsYMatrix:=dsValid;
  Result:=True;
  EditYMatrix(True);
end;

function TMathPlan.OpenExec;
var
  DriveDir,Name,Ext : string;
begin
  Result := OpenDialog.Execute;
  OpenResult := Result;
  if(not Result) then
    Exit;
  SplitPath(OpenDialog.FileName,DriveDir,Name,Ext);
  StripLeadingAndTrailing(DriveDir);
  StripLeadingAndTrailing(Name);
  StripLeadingAndTrailing(Ext);
  if Ext = '' then
  begin
    if OpenDialog.Filter = StrTXTFilter then
    begin
      if OpenDialog.FilterIndex = 1 then
        Ext := 'txt';
    end else
    if OpenDialog.Filter = StrGRDFilter then
    begin
      if OpenDialog.FilterIndex = 1 then
        Ext := 'grd';
    end else
    if OpenDialog.Filter = StrMPLFilter then
    begin
      if OpenDialog.FilterIndex = 1 then
        Ext := 'mpl';
    end;
  end;
  OpenDialog.FileName:=MakePath(DriveDir,Name,Ext);
end;

function TMathPlan.SaveExec;
var
  DriveDir,Name,Ext : string;
begin
  Result:=SaveDialog.Execute;
  SaveResult:=Result;
  if(not Result) then exit;
  SplitPath(SaveDialog.FileName,DriveDir,Name,Ext);
  StripLeadingAndTrailing(DriveDir);
  StripLeadingAndTrailing(Name);
  StripLeadingAndTrailing(Ext);
  if Ext = '' then
  begin
    if SaveDialog.Filter = StrTXTFilter then
    begin
      if SaveDialog.FilterIndex = 1 then
        Ext := 'txt';
    end else
    if SaveDialog.Filter = StrGRDFilter then
    begin
      if SaveDialog.FilterIndex = 1 then
        Ext := 'grd';
    end else
    if SaveDialog.Filter = StrMPLFilter then
    begin
      if SaveDialog.FilterIndex = 1 then
        Ext := 'mpl';
    end;
  end;
  SaveDialog.FileName:=MakePath(DriveDir,Name,Ext);
end;

// =======================================================================
// Создание форм, в которых идет редактирование матриц и векторов
// =======================================================================
procedure TMathPlan.CreateCMForm;
begin
  if(CMForm=nil) then
  begin
    CMForm:=TMTEditForm.Create(Application.MainForm);
    CMForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
    CMForm.OnChangeCells:=SynchronizeMatrices;
    CMForm.Caption:='Кодовая матрица';
  end;
end;

procedure TMathPlan.CreateNMForm;
begin
  if(NMForm=nil) then
  begin
    NMForm:=TMTEditForm.Create(Application.MainForm);
    NMForm.MatrixGrid.Options:=NMForm.MatrixGrid.Options-[goEditing];
    NMForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
    NMForm.Caption:='Матрица планирования';
  end;
end;

procedure TMathPlan.CreateMLForm;
begin
  if(MLForm=nil) then
  begin
    MLForm:=TMTEditForm.Create(Application.MainForm);
    MLForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
    MLForm.OnChangeCells:=SynchronizeMatrices;
    MLForm.Caption:='Основные уровни';
  end;
end;

procedure TMathPlan.CreateDFForm;
begin
  if(DFForm=nil) then
  begin
    DFForm:=TMTEditForm.Create(Application.MainForm);
    DFForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
    DFForm.OnChangeCells:=SynchronizeMatrices;
    DFForm.Caption:='Интервалы варьирования';
  end;
end;

procedure TMathPlan.CreateYForm;
begin
  if(YForm=nil) then
  begin
    YForm:=TMTEditForm.Create(Application.MainForm);
    YForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
    YForm.Caption:='Отклики';
  end;
end;

procedure TMathPlan.CreateReportForm;
begin
  if(ReportForm=nil) then
  begin
    ReportForm:=TReportForm.Create(Application.MainForm);
    ReportForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  end;
end;

// =======================================================================
// Уничтожение форм, в которых идет редактирование матриц и векторов
// =======================================================================
procedure TMathPlan.DestroyCMForm;
begin
  if(CMForm<>nil) then
  begin
    CMForm.Disconnect;
    CMForm.Destroy;
    CMForm:=nil;
  end;
end;

procedure TMathPlan.DestroyMLForm;
begin
  if(MLForm<>nil) then
  begin
    MLForm.Disconnect;
    MLForm.Destroy;
    MLForm:=nil;
  end;
end;

procedure TMathPlan.DestroyDFForm;
begin
  if(DFForm<>nil) then
  begin
    DFForm.Disconnect;
    DFForm.Destroy;
    DFForm:=nil;
  end;
end;

procedure TMathPlan.DestroyNMForm;
begin
  if(NMForm<>nil) then
  begin
    NMForm.Disconnect;
    NMForm.Destroy;
    NMForm:=nil;
  end;
end;

procedure TMathPlan.DestroyYForm;
begin
  if(YForm<>nil) then
  begin
    YForm.Disconnect;
    YForm.Destroy;
    YForm:=nil;
  end;
end;

procedure TMathPlan.DestroyReportForm;
begin
  if(ReportForm<>nil) then
  begin
    ReportForm.Destroy;
    ReportForm:=nil;
  end;
end;

procedure TMathPlan.DestroyForms;
begin
  DestroyCMForm;
  DestroyNMForm;
  DestroyMLForm;
  DestroyDFForm;
  DestroyYForm;
  DestroyReportForm;
end;

// =======================================================================
// Управление формой, в которой идет редактирование кодовой матрицы
// =======================================================================
procedure TMathPlan.EditCodeMatrix(OkCancel:Boolean);
begin
  if(OkCancel) then
  begin
    if(dsMainMatrix<>dsUndeterminate) then
    begin
      CreateCMForm;
      CMForm.AttachMatrix(@MainMatrix);
      if(CMForm.WindowState=wsMinimized) then
        CMForm.WindowState:=wsNormal;
    end;
  end
  else
    CMForm.WindowState:=wsMinimized;
end;

// =======================================================================
// Управление формой, в которой идет редактирование вектора факторов
// =======================================================================
procedure TMathPlan.EditMLVector(OkCancel:Boolean);
begin
  if(OkCancel) then
  begin
    if(dsMLVector<>dsUndeterminate) then
    begin
      CreateMLForm;
      MLForm.AttachVector(@MLVector);
      if(MLForm.WindowState=wsMinimized) then
        MLForm.WindowState:=wsNormal;
    end;
  end
  else
    MLForm.WindowState:=wsMinimized;
end;

// =======================================================================
// Управление формой, в которой идет редактирование вектора интервалов в-я
// =======================================================================
procedure TMathPlan.EditDFVector(OkCancel:Boolean);
begin
  if(okCancel) then
  begin
    if(dsDFVector<>dsUndeterminate) then
    begin
      CreateDFForm;
      DFForm.AttachVector(@DFVector);
      if(DFForm.WindowState=wsMinimized) then
        DFForm.WindowState:=wsNormal;
    end;
  end
  else
    DFForm.WindowState:=wsMinimized;
end;

// =======================================================================
// Управление формой, в которой идет редактирование натуральной матрицы
// =======================================================================
procedure TMathPlan.EditNaturalMatrix(OkCancel:Boolean);
begin
  if(okCancel) then
  begin
    if(dsNaturalMatrix<>dsUndeterminate) then
    begin
      CreateNMForm;
      NMForm.AttachMatrix(@NaturalMatrix);
      if(NMForm.WindowState=wsMinimized) then
        NMForm.WindowState:=wsNormal;
    end;
  end
  else
    NMForm.WindowState:=wsMinimized;
end;

// =======================================================================
// Управление формой, в которой идет редактирование матрицы откликов
// =======================================================================
procedure TMathPlan.EditYMatrix(OkCancel:Boolean);
begin
  if(okCancel) then
  begin
    if(dsYMatrix<>dsUndeterminate) then
    begin
      CreateYForm;
      YForm.AttachMatrix(@YMatrix);
      if(YForm.WindowState=wsMinimized) then
        YForm.WindowState:=wsNormal;
    end;
  end
  else
    YForm.WindowState:=wsMinimized;
end;

procedure TMathPlan.SetFormEditing(Name:string;Editing:Boolean);
begin
  if(Name=FormNames[0]) then
    EditCodeMatrix(Editing)
  else if(Name=FormNames[1]) then
    EditNaturalMatrix(Editing)
  else if(Name=FormNames[2]) then
    EditMLVector(Editing)
  else if(Name=FormNames[3]) then
    EditDFVector(Editing)
  else if(Name=FormNames[4]) then
    EditYMatrix(Editing)
  else if(Name=FormNames[5]) then
  begin
    if(Editing) then ShowReport else HideReport;
  end
  else if(Name=FormNames[6]) then
  begin
    if(GetGServer<>nil) then
    begin
      if(Editing and GetGServer^.CanDo(acShow)) then
        GetGServer^.Show(True);
      if((not Editing) and GetGServer^.CanDo(acHide)) then
        GetGServer^.Show(False);
    end;
  end;
end;

procedure TMathPlan.OnChangeSetup;
begin
  ShowDialog.CBShowAlways.Checked:=AppSetup.GeneralFlags.ShowShowList;
  MinimizeDialog.CBShowAlways.Checked:=AppSetup.GeneralFlags.ShowHideList;
  ArrangeDialog.CBShowAlways.Checked:=
    AppSetup.GeneralFlags.ShowArrangeList;
  if(CMForm<>nil) then
    CMForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  if(NMForm<>nil) then
    NMForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  if(MLForm<>nil) then
    MLForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  if(DFForm<>nil) then
    DFForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  if(YForm<>nil) then
    YForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  if(ReportForm<>nil) then
    ReportForm.ToolBar.ShowHint:=AppSetup.GeneralFlags.ShowHints;
  if(GetGServer<>nil) then
    GetGServer^.SetupChanged;
end;

procedure TMathPlan.SynchronizeMatrices;
begin
  if((dsMLVector=dsUndeterminate)or(dsDFVector=dsUndeterminate)) then
    exit;
  NaturalFromCode;
  if(NMForm<>nil) then NMForm.AttachMatrix(@NaturalMatrix);
end;

// =======================================================================
// Расчет средних, дисперсий, стандартных отклонений и критерия Кохрена
// =======================================================================
function	TMathPlan.BeforeCohrenAnalysis:Boolean;
var
  i,j    : integer;
  s      : double;
begin
  // Если полное число опытов не определено, то проявим проблеск разума
  if(dsN<>dsValid) then
  begin
    N:=YMatrix.nRows;
    dsN:=dsValid;
  end;
  // Если не определено звездное плечо, вычисляем его
  if(pType in [ptCCO,ptCCR]) then
  begin
    Alpha:=GetAlpha(pType);
    dsAlpha:=dsValid;
  end;
  // Если векторы средних значений, дисперсий и ошибок еще не созданы,
  // то создаем их.
  if(AVector.nSize<>N) then AVector.ReCreate(N);
  if(DVector.nSize<>N) then DVector.ReCreate(N);
  if(SVector.nSize<>N) then SVector.ReCreate(N);
  dsAVector:=dsUndeterminate;
  dsDVector:=dsUndeterminate;
  dsSVector:=dsUndeterminate;
  dsCH_R:=dsUndeterminate;
  dsCH_T:=dsUndeterminate;
  dsDV:=dsUndeterminate;
  dsDMax:=dsUndeterminate;
  // Заполняем вектор средних значений
  for i:=0 to N-1 do
  begin
    s:=0;
    for j:=0 to R-1 do s:=s+YMatrix.Elements^[i]^[j];
    AVector.Elements^[i]:=s/R;
  end;
  dsAVector:=dsValid;
  Result:=True;
end;

function TMathPlan.CohrenAnalysis;
var
  MsgBeg : string;
  i,j    : integer;
  s      : double;
begin
  MsgBeg:='Анализ однородности дисперсий невозможен:'+#13;
  Result:=Fail(acAnalysis);
  if not BeforeCohrenAnalysis then
  	Exit;
  // Если статистический анализ невозможен (число повторений == 1) то выход
  if(R=1) then begin
	Result:=Success(acAnalysis);
    exit;
  end;
  // Заполняем векторы дисперсий и ошибок, находим максимальную дисперсию
  DV:=0;
  DMax:=0;
  for i:=0 to N-1 do
  begin
    s:=0;
    for j:=0 to R-1 do
      s:=s+sqr(YMatrix.Elements^[i]^[j]-AVector.Elements^[i]);
    DVector.Elements^[i]:=s/(R-1);
    DV:=DV+DVector.Elements^[i];
    SVector.Elements^[i]:=sqrt(DVector.Elements^[i]);
    if(DMax<DVector.Elements^[i]) then DMax:=DVector.Elements^[i];
  end;
  dsDVector:=dsValid;
  dsSVector:=dsValid;
  dsDMax:=dsValid;
  if(DV=0.0) then
    CH_R:=0
  else
    CH_R:=DMax/DV;
  DV:=DV/N;
  dsDV:=dsValid;
  dsCH_R:=dsValid;
  GetCohren(R-1,N,CH_T);
  dsCH_T:=dsValid;
  Result:=Success(acAnalysis);
  MainForm.StateChanged;
  if(AppSetup.GeneralFlags.CohrenResultsShow {and (R>1)}) then
  begin
    CohrenCompleteForm.ESqrtDV.Text:=FloatToStr(Sqrt(DV));
    CohrenCompleteForm.ECH_R.Text:=FloatToStr(CH_R);
    CohrenCompleteForm.ECH_T.Text:=FloatToStr(CH_T);
    if(CH_R>CH_T) then
      CohrenCompleteForm.EResult.Text:='Неоднородны.'
    else
      CohrenCompleteForm.EResult.Text:='Однородны.';
    CohrenCompleteForm.ShowModal;
  end;
  if(AppSetup.GeneralFlags.CohrenActivateReport) then
  begin
    CreateReport;
    ShowReport;
  end;
end;

function TMathPlan.RegenerateCode;
begin
  Result:=Fail(acRegenerateCode);
  if(CanDo(acRegenerateCode)) then
  begin
    CreateCodeMatrix;
    Result:=Success(acRegenerateCode);
    if(CMForm<>nil) then
      CMForm.AttachMatrix(@MainMatrix);
    SynchronizeMatrices(Self);
    MainForm.StateChanged;
  end;
end;

// =======================================================================
// Расчет  критерия Стьюдента; отсев коэффициентов, корректировка расши-
// ренной матрицы плана и пересчет коэффициентов.
// =======================================================================
function TMathPlan.StudentAnalysis;
var
  MsgBeg : string;
  i,j,k  : integer;
  s,det  : double;
  Resp   : longint;
  Xt     : TStreamableMatrix;
  L      : TStreamableMatrix;
  Enew   : TStreamableMatrix;
  Bnew   : TStreamableVector;
  ST_T   : double;
label
  StudentAnalysisFailed,
  StudentAnalysisSuccessed;
begin
  Result:=Fail(acAnalysis);
  MsgBeg:='Расчет параметров модели невозможен:'+#13;
  Xt.Create(0,0);
  L.Create(0,0);
  Enew.Create(0,0);
  AddenUsed.Clear;
  Bnew.Create(0);
  // Требуются данные:
  //  - матрица плана
  //  - вектор средних значений наблюдений
  //  - дисперсия воспроизводимости
  if(dsAVector=dsUndeterminate) then
  begin
    CohrenAnalysis;
    if(dsAvector=dsUndeterminate) then begin
      Warning('Не удается вычислить вектор средних откликов.');
      goto StudentAnalysisFailed;
    end;
  end; // if dsAVector=dsUndeterminate
  // Средние есть.
  // Если ковариационной матрицы нет, то пытаемся создать ее сейчас.
  if(True{dsCMatrix=dsUndeterminate}) then
  begin
    // Если информационной матрицы нет, то пытаемся создать ее сейчас.
    if(True{dsMMatrix=dsUndeterminate}) then
    begin
      if(dsMainMatrix=dsUndeterminate) then
      begin
        // Первичная матрица не определена - жалуемся и на выход
        Warning(MsgBeg+'не определена кодовая матрица плана');
        goto StudentAnalysisFailed;
      end;
      // Составляем расширенную матрицу плана
      ExpandCodeMatrix;
      if(dsExpandedMatrix=dsUndeterminate) then
      begin
        Warning(MsgBeg+'Ошибка составления матрицы базисных функций');
        goto StudentAnalysisFailed;
      end;
      Xt.Recreate(ExpandedMatrix.nRows,ExpandedMatrix.nCols);
      Xt.CopyFrom(ExpandedMatrix);
      Xt.Transpose;
      MMatrix.Recreate(Xt.nRows,ExpandedMatrix.nCols);
      MMatrix.Mul(Xt,ExpandedMatrix);
      dsMMatrix:=dsValid;
    end; // if dsMatrix=dsUndeterminate
    // Есть информационная матрица.
    CMatrix.Recreate(MMatrix.nRows,MMatrix.nCols);
    CMatrix.CopyFrom(MMatrix);
    CMatrix.Duplicate;
    det:=CMatrix.Determ;
    if(abs(det)<1e-15) then
    begin
      if(det=0.0) then
      begin
        Warning('Информационая матрица вырождена.'+#13+
          'Продолжение расчета невозможно.');
        goto StudentAnalysisFailed;
      end;
      Resp:=AskYN('Информационая матрица плохо обусловлена. Коэффициенты'+
        ' в уравнении регрессии будут иметь высокие погрешности'+
        #13+'Продолжить расчет - ?');
      if(Resp<>idYes) then
        goto StudentAnalysisFailed;
    end;
    CMatrix.Restore;
    CMatrix.Inverse;
    dsCMatrix:=dsValid;
  end; // if dsCMatrix=dsUndeterminate
  // Есть ковариационная матрица.
  if((Xt.nCols<>ExpandedMatrix.nRows)or(Xt.nRows<>ExpandedMatrix.nCols))
  then begin
    Xt.Recreate(ExpandedMatrix.nRows,ExpandedMatrix.nCols);
    Xt.CopyFrom(ExpandedMatrix);
    Xt.Transpose;
  end;
  L.Create(CMatrix.nRows,Xt.nCols);
  L.Mul(CMatrix,Xt);
  BVector.Recreate(L.nRows);
  AddenUsed.Count:=BVector.nSize;
  L.VectorMul(AVector,BVector);
  // Есть коэффициенты модели.
  // Если статистический анализ невозможен (число повторений == 1) то выход
  ActualBSize:=BVector.nSize;
  if(R=1) then begin
	for i:=0 to BVector.nSize-1 do
		AddenUsed[i]:=pointer(True);
	FillBNames;
    goto StudentAnalysisSuccessed;
  end;
  // Проверяем коэффициенты на значимость.
  // Ошибки коэффицентов - на диагонали ковариационной матрицы.
  // Число степеней свободы - N(R-1)
  GetStudent(N*(R-1),ST_T);
  ExpandedMatrix.Duplicate;
  for i:=0 to BVector.nSize-1 do
  begin
    s:=DV*CMatrix.Elements^[i]^[i];
    s:=sqrt(s);
    if(abs(BVector.Elements^[i])<ST_T*s) then // Коэффициент незначим
    begin
      Dec(ActualBSize);
      AddenUsed[i]:=pointer(False);
    end
    else
      AddenUsed[i]:=pointer(True);
  end;
  // Все коэффициенты значимы.
  if(ActualBSize = BVector.nSize) then
    goto StudentAnalysisSuccessed;
(* 25.01.2012
  // Нет ни одного значимого коэффициента
  if(ActualBSize=0) then
  begin
    Warning('В уравнении регрессии нет ни одного '+#13+
      'значимого коэффициента.');
    ExpandedMatrix.Restore;
    dsBVector:=dsValid;
    dsActualBSize:=dsValid;
    goto StudentAnalysisFailed;
  end;
  // Если не все коэффициенты значимы, то предоставляем пользователю
  // возможность выбрать действия. Если ни одного значимого коэффициента
  // нет вообще - не предоставляем.
*)
  if(ActualBSize = 0) then
  begin
    ModelChangeForm.RGroup.ItemIndex := 0;
    ModelChangeForm.RGroup.Enabled := False;
    ModelChangeForm.Caption := 'Все параметры модели статистически незначимы !';
  end
  else
  begin
    ModelChangeForm.RGroup.Enabled := True;
    ModelChangeForm.Caption := 'Некоторые параметры модели статистически незначимы';
  end;
(* end 25.01.2012 *)
  ModelChangeForm.ShowModal;
  if(ModelChangeForm.ModalResult = mrCancel) then
  begin
    dsExpandedMatrix:=dsUndeterminate;
    dsMMatrix:=dsUndeterminate;
    dsCMatrix:=dsUndeterminate;
    dsBVector:=dsUndeterminate;
    dsActualBSize:=dsUndeterminate;
    goto StudentAnalysisFailed;
  end;
  case ModelChangeForm.RGroup.ItemIndex of
    0: // Игнорировать
      begin
        ActualBSize:=BVector.nSize;
        ExpandedMatrix.Restore;
      end;
    1: // Обнулить
      begin
        ExpandedMatrix.Restore;
        for i:=0 to BVector.nSize-1 do
        begin
          if(Boolean(AddenUsed[i])=False) then
            BVector.SetElement(i,0);
        end;
        FillBNames;
      end;
    2: // Пересчитать
      begin
        // Пересчитываем коэффициенты.
        Enew.Create(MainMatrix.nRows,ActualBSize);
        // Те столбцы, которые соответствуют ненулевым элементам маски,
        // копируем в новую матрицу
        k:=0;
        for i:=0 to AddenUsed.Count-1 do
        begin
          if(Boolean(AddenUsed[i])) then
          begin
            for j:=0 to ExpandedMatrix.nRows do
              Enew.Elements^[j][k]:=ExpandedMatrix.Elements^[j][i];
            k:=k+1;
          end;
        end;
        ExpandedMatrix.Recreate(MainMatrix.nRows,ActualBSize);
        ExpandedMatrix.CopyFrom(Enew);
        Xt.Recreate(ExpandedMatrix.nRows,ExpandedMatrix.nCols);
        Xt.CopyFrom(ExpandedMatrix);
        Xt.Transpose;
        MMatrix.Recreate(Xt.nRows,ExpandedMatrix.nCols);
        MMatrix.Mul(Xt,ExpandedMatrix);
        CMatrix.Recreate(Xt.nRows,ExpandedMatrix.nCols);
        CMatrix.CopyFrom(MMatrix);
        CMatrix.Duplicate;
        det:=CMatrix.Determ;
        if(abs(det)<1e-15) then
        begin
          if(det=0.0) then
          begin
            Warning('Информационая матрица вырождена.'+#13+
              'Продолжение расчета невозможно.');
            goto StudentAnalysisFailed;
          end;
          Resp:=AskYN('Информационая матрица очень плохо обусловлена.'+
            #13+#13+'Продолжить расчет - ?');
          if(Resp<>idYes) then
            goto StudentAnalysisFailed;
        end;
        CMatrix.Restore;
        CMatrix.Inverse;
        L.Recreate(CMatrix.nRows,Xt.nCols);
        L.Mul(CMatrix,Xt);
        Bnew.Create(ActualBSize);
        L.VectorMul(AVector,Bnew);
        j:=0;
        for i:=0 to BVector.nSize-1 do
        begin
          if(Boolean(AddenUsed[i])) then
          begin
            BVector.Elements^[i]:=Bnew.Elements^[j];
            j:=j+1;
          end
          else BVector.Elements^[i]:=0.0;
        end;
        FillBNames;
      end;
  end;
StudentAnalysisSuccessed:
  dsBVector:=dsValid;
  dsActualBSize:=dsValid;
  Result:=Success(acAnalysis);
  MainForm.StateChanged;
  // R: 25.01.00
  if(AppSetup.GeneralFlags.StudentResultsShow and (R>1)) then
  begin
    StudentCompleteForm.EActualBSize.Text:=FloatToStr(ActualBSize);
    StudentCompleteForm.ShowModal;
  end;
  if(AppSetup.GeneralFlags.StudentActivateReport) then
  begin
    CreateReport;
    ShowReport;
  end;
StudentAnalysisFailed:
  Bnew.Destroy;
  Enew.Destroy;
  Xt.Destroy;
  L.Destroy;
end;

// =======================================================================
// Вычисление значения в указанной точке кодового пространства
// =======================================================================
function TMathPlan.GetYC;
var
  i,j   : integer;
  count : integer;
  res   : double;
begin
  if(pType=ptFromWizard) then
  begin
    if(Model<>nil) then
    begin
      Model^.Value(V.Elements,BVector.Elements,res);
      Result:=res;
    end
    else
    begin
      Result:=0;
      for i:=0 to Addens.Count-1 do
      begin
        PAdden(Addens[i])^.Value(V.Elements,res);
        Result:=Result+BVector.Elements^[i]*res;
      end;
    end;
    exit;
  end;
  Result:=BVector.Elements^[0];
  count:=1;
  // Линейные слагаемые
  for i:=0 to Degree-1 do
  begin
    Result:=Result+BVector.Elements^[count]*V.Elements^[i];
    count:=count+1;
  end;
  if(count>=BVector.nSize) then exit;
  // Эффекты взаимодействия
  for i:=0 to Degree-2 do
    for j:=i+1 to Degree-1 do
    begin
      Result:=Result+BVector.Elements^[count]*V.Elements^[i]*
        V.Elements^[j];
      count:=count+1;
    end;
  // Квадратичные эффекты
  for i:=0 to Degree-1 do
  begin
    Result:=Result+BVector.Elements^[count]*V.Elements^[i]*V.Elements^[i];
    count:=count+1;
  end;
end;

// =======================================================================
// GetY
// =======================================================================
procedure TMathPlan.GetY;
var
  Fiction : TVector;
begin
  if((Pt<>nil)and(Res<>nil)) then
  begin
    Fiction.nSize:=Degree;
    Fiction.Elements:=Pt;
    Res^:=GetYC(Fiction);
  end;
end;

// =======================================================================
// GetMainFormHandle
// =======================================================================
function TMathPlan.GetMainFormHandle;
begin
  Result:=MainUnit.MainForm.Handle;
end;

// =======================================================================
// 23.11.99. ВНИМАНИЕ - накаких проверок не выполняется !
// =======================================================================

function  TMathPlan.GetCurrentGen:PGen;
begin
  Result:=Gen;               // NULL if pType != ptFromWizard
end;

function  TMathPlan.GetCurrentModel:PModel;
begin
  Result:=Model;             // NULL if pType != ptFromWizard
end;

function  TMathPlan.GetModelAdden(nAdden:integer):PAdden;
begin
  if(Model<>nil) then
    Result:=Model^.Addens(nAdden)
  else
    Result:=PAdden(Addens[nAdden]);
end;

function TMathPlan.GetCurrentBVector:PDouble;
begin
  Result:=PDouble(BVector.Elements);
end;

// =======================================================================
// Поиск экстремальных значений
// =======================================================================
procedure TMathPlan.FindMin;
// Локальные процедуры для метода покоординатного спуска
  // Генерация нового набора точек
  procedure CreateStar(var Center,Delta:TVector;var Star:TMatrix);
  var
    r,c : integer;
  begin
    Assert(Center.nSize=Star.nCols);
    Assert(Center.nSize=Delta.nSize);
    Assert(Star.nRows=(Star.nCols shl 1));
    for r:=0 to Star.nRows-1 do
    begin
      for c:=0 to Star.nCols-1 do Star.SetElement(r,c,Center.Elements[c]);
      if((r mod 2)=0) then
        Star.SetElement(r,r shr 1,
          Star.GetElement(r,r shr 1)+Delta.GetElement(r shr 1))
      else
        Star.SetElement(r,r shr 1,
          Star.GetElement(r,r shr 1)-Delta.GetElement(r shr 1));
    end;
  end;
  // Выяснение принадлежности точки прямоугольной области
  function PointInBox(var Min,Max,Pt:TVector):Boolean;
  var
    i : integer;
  begin
    Assert(Min.nSize=Max.nSize);
    Assert(Min.nSize=Pt.nSize);
    for i:=0 to Pt.nSize-1 do
    begin
      if((Pt.GetElement(i)<Min.GetElement(i))or
         (Max.GetElement(i)<Pt.GetElement(i))) then
      begin
        Result:=False;
        exit;
      end;
    end;
    Result:=True;
  end;
  var
    TempVec : TVector;
  // Поиск точки с минимальным откликом. Если такой точки нет, то
  // возвращается Лож, иначе - Истина и новая точка - в векторе Center.
  function FindBest(var Start,Stop,Center:TVector;var Star:TMatrix):Boolean;
  var
    i : integer;
    value, val2 : double;
  begin
    Result:=False;
    Assert(Center.nSize=Star.nCols);
    Assert(Center.nSize=TempVec.nSize);
    Assert(Star.nRows=(Star.nCols shl 1));
    value:=GetYC(Center);
    for i:=0 to Star.nRows-1 do
    begin
      Star.RowToVector(TempVec,i,0);
      val2:=GetYC(TempVec);
      if(PointInBox(Start,Stop,TempVec) and (val2<value)) then
      begin
        Result:=True;
        value:=val2;
        Center._CopyFrom(TempVec);
      end;
    end;
  end;
var
  total, done : integer;
  Progress : integer;
  PrevProgress : integer;
  Point : TVector;
  ExValue : double;
  Value : double;
  // Обновление индикатора но полям total и done
  function UpdateProgress : Boolean;
  var
    Flash : Boolean;
  begin
    Result:=False;
    Progress:=trunc(100*done/total);
    if(Progress<>PrevProgress) then
    begin
      Sleep(5);
      ExProgress.Gauge.Progress:=Progress;
      ExProgress.Edit.Text:=FloatToStr(ExValue);
      PrevProgress:=Progress;
      Windows.UpdateWindow(ExProgress.Handle);
      if(((Windows.GetAsyncKeyState(VK_ESCAPE) and $8000)<>0)or
        ExProgress.Cancel) then
      begin
        Result:=True;
        ExProgress.StatusBar.SimpleText:=
          'Вы можете отпустить кнопку ''Esc''...';
        Windows.UpdateWindow(ExProgress.Handle);
        Sleep(30);
        Flash:=False;
        while (Windows.GetAsyncKeyState(VK_RETURN) and $8000)<>0 do
        begin
          Sleep(200);
          FlashWindow(ExProgress.Handle,Flash);
          Flash:= not Flash;
        end;
        RemoveMessages(ExProgress.Handle);
        exit;
      end;
      if(((Windows.GetAsyncKeyState(VK_RETURN) and $8000)<>0)or
        ExProgress.Stop) then
      begin
        Result:=True;
        MainPoint._CopyFrom(Point);
        dsMainPoint:=dsValid;
        ExProgress.StatusBar.SimpleText:=
          'Вы можете отпустить кнопку ''Enter''...';
        Windows.UpdateWindow(ExProgress.Handle);
        Sleep(30);
        Flash:=False;
        while (Windows.GetAsyncKeyState(VK_RETURN) and $8000)<>0 do
        begin
          Sleep(200);
          FlashWindow(ExProgress.Handle,Flash);
          Flash:= not Flash;
        end;
        RemoveMessages(ExProgress.Handle);
        exit;
      end;
    end;
    done:=done+1;
  end;
label
  CleanUp;
var
  Deltas : TVector;
  Star : TMatrix;
  i,j,k,l,i4 : integer;
begin
  dsMainPoint:=dsUndeterminate;
  if(dsBVector=dsUndeterminate) then
  begin
    exit;
  end;
  // Если число факторов больше 4 - предлагаем использовать метод МК
  if((Degree>4)and((Method<>mtRandom))) then
  begin
    if(AskYN('Число факторов слишком велико для выбранного метода.'
      +#13+'Использовать метод Монте-Карло - ?')=idNo) then exit;
    Method:=mtRandom;
  end;
  // Диапазоны изменения элементов
  Deltas.Create(Degree);
  Deltas._CopyFrom(Stop);
  Deltas.Sub(Start);
  // Первое приближение - центр исследуемой области, если до этого поиск
  // не производился, либо последний нахденный экстремум
  Point.Create(Degree);
  if((dsMainPoint=dsUndeterminate)or(not UseOld)) then
  begin
    Point._CopyFrom(Start);
    Point.Add(Stop);
    Point.ScalarMul(0.5);
  end
  else
  begin
    Point._CopyFrom(MainPoint);
  end;
  ExValue:=GetYC(Point);
  // Точка оптимуме
  if(MainPoint.nSize<>Degree) then
    MainPoint.Recreate(Degree);
  dsMainPoint:=dsUndeterminate;
  MainPoint._CopyFrom(Point);
  ExProgress.Gauge.Progress:=0;
  ExProgress.Edit.Text:=FloatToStr(ExValue);
  ExProgress.StatusBar.SimpleText:=
    'Для остановки нажмите и удерживайте ''Enter'' или ''Esc''';
  while (Windows.GetAsyncKeyState(VK_RETURN) and $8000)<>0 do;
  done:=0;
  PrevProgress:=0;
  Progress:=0;
  case Method of
    // ===================================================================
    mtRandom:
    begin
      // Полное количество операций
      total:=Trunc(CusData/50);
      ExProgress.Caption:='Поиск экстремума: метод Монте-Карло.';
      ExProgress.Show;
      Randomize;
      // Слишком частые вызовы для обновления индикатора -
      // непроизводительная трата времени. Здесь мы будем обновнять
      // индикатор каждую 50-ю итерацию.
      for i:=1 to Trunc(CusData/50) do
      begin
        for j:=1 to 50 do
        begin
          // Заполняем вектор случайными числами
          for i4:=0 to Degree-1 do
            Point.SetElement(i4,Start.GetElement(i4)+
              Deltas.GetElement(i4)*Random(1000)/1000);
          // Вычисляем значение в этой точке
          Value:=GetYC(Point);
          if(Value<ExValue) then
          begin
            ExValue:=Value;
            MainPoint._CopyFrom(Point);
          end;
        end;
        // Обновляем индикатор
        if(UpdateProgress) then goto CleanUp;
      end;
      dsMainPoint:=dsValid;
    end;
    // ===================================================================
    mtScan:
    begin
      ExProgress.Caption:='Поиск экстремума: метод сканирования.';
      ExProgress.Show;
      total:=1;
      // Дополнительные данные: количество шагов разбиения
      // Величины приращений
      for i:=0 to Degree-1 do
      begin
        Deltas.SetElement(i,Deltas.GetElement(i)/CusData);
        total:=total*CusData;
      end;
      total:=Trunc(total/CusData);
      case Degree of
        1:
        begin
          Assert(False);
        end;
        2:
        begin
          Point.Elements^[0]:=Start.Elements^[0];
          for i:=0 to CusData do
          begin
            Point.Elements^[1]:=Start.Elements^[1];
            for j:=0 to CusData do
            begin
              Value:=GetYC(Point);
              if(Value<ExValue) then
              begin
                ExValue:=Value;
                MainPoint._CopyFrom(Point);
              end;
              Point.Elements^[1]:=Point.Elements^[1]+Deltas.Elements^[1];
            end;
            if(UpdateProgress) then goto CleanUp;
            Point.Elements^[0]:=Point.Elements^[0]+Deltas.Elements^[0];
          end;
        end;
        3:
        begin
          Point.Elements^[0]:=Start.Elements^[0];
          for i:=0 to CusData do
          begin
            Point.Elements^[1]:=Start.Elements^[1];
            for j:=0 to CusData do
            begin
              Point.Elements^[2]:=Start.Elements^[2];
              for k:=0 to CusData do
              begin
                Value:=GetYC(Point);
                if(Value<ExValue) then
                begin
                  ExValue:=Value;
                  MainPoint._CopyFrom(Point);
                end;
                Point.Elements^[2]:=Point.Elements^[2]+Deltas.Elements^[2];
              end;
              Point.Elements^[1]:=Point.Elements^[1]+Deltas.Elements^[1];
              if(UpdateProgress) then goto CleanUp;
            end;
            Point.Elements^[0]:=Point.Elements^[0]+Deltas.Elements^[0];
          end;
        end;
        4:
        begin
          Point.Elements^[0]:=Start.Elements^[0];
          for i:=0 to CusData do
          begin
            Point.Elements^[1]:=Start.Elements^[1];
            for j:=0 to CusData do
            begin
              Point.Elements^[2]:=Start.Elements^[2];
              for k:=0 to CusData do
              begin
                Point.Elements^[3]:=Start.Elements^[3];
                for l:=0 to CusData do
                begin
                  Value:=GetYC(Point);
                  if(Value<ExValue) then
                  begin
                    ExValue:=Value;
                    MainPoint._CopyFrom(Point);
                  end;
                  Point.Elements^[3]:=Point.Elements^[3]+
                    Deltas.Elements^[3];
                end;
                Point.Elements^[2]:=Point.Elements^[2]+Deltas.Elements^[2];
                if(UpdateProgress) then goto CleanUp;
              end;
              Point.Elements^[1]:=Point.Elements^[1]+Deltas.Elements^[1];
            end;
            Point.Elements^[0]:=Point.Elements^[0]+Deltas.Elements^[0];
          end;
        end;
        else Assert(False);
      end;
      dsMainPoint:=dsValid;
    end;
    mtHaussZeidel:
    begin
      TempVec.Create(Degree);
      Star.Create(Degree shl 1,Degree);
      // Полное количество операций варьируется в пределах от
      // NumSteps до NumSteps ^ Degree (если поверхность очень сложная)
      // На практике можно принять total = 2 * NumSteps
      total:=2 * CusData;
      // Дополнительные данные: количество шагов разбиения
      // Величины приращений
      for i:=0 to Degree-1 do
      begin
        Deltas.SetElement(i,Deltas.GetElement(i)/CusData);
      end;
      done:=0;
      ExProgress.Caption:='Поиск экстремума: метод Гаусса - Зейделя.';
      ExProgress.Show;
      // Будем обновнять индикатор каждую 50-ю итерацию.
      i:=0;
      while(True) do
      begin
        CreateStar(Point,Deltas,Star);
        if(not FindBest(Start,Stop,Point,Star)) then
        begin
          // Екстремум найден.
          MainPoint._CopyFrom(Point);
          dsMainPoint:=dsValid;
          goto CleanUp;
        end;
        i:=i+1;
        done:=done+1;
        if(i=50) then
        begin
          i:=0;
          if(UpdateProgress) then goto CleanUp;
        end;
      end;
      Star.Destroy;
      TempVec.Destroy;
    end;
  end; // case
CleanUp:
  ExProgress.Close;
  Point.Destroy;
  Deltas.Destroy;
end;

procedure TMathPlan.FindMax;
var
  i : integer;
begin
  // Поиск максимума - это поиск минимума модели с противоположными
  // коэффициентами.
  for i:=0 to BVector.nSize-1 do
    BVector.Elements^[i]:=-BVector.Elements^[i];
  FindMin(Start, Stop, CusData, UseOld, Method);
  for i:=0 to BVector.nSize-1 do
    BVector.Elements^[i]:=-BVector.Elements^[i];
end;

// Расчет невязок, дисперсии адекватности и критерия Фишера.
function TMathPlan.FisherAnalysis;
var
  Pc,DCP  : TVector;
  ECP     : TVector;
  i       : integer;
  Resp    : integer;
  MsgBeg  : string;
  MsgUserAbort : string;
  nb      : integer;
  Ycp     : double;
begin
  // 25.01.00
  if((dsR=dsValid)and(R=1)) then begin
    CreateReport;
    ShowReport;
    Result:=Success(acAnalysis);
    exit;
  end;
  Result:=Fail(acAnalysis);
  // Требуются данные:
  //  - матрица планирования в кодовой форме
  //  - число повторений каждого опыта
  //  - вектор средних значений откликов
  //  - вектор коэффициентов
  //  - число значимых коэффициентов
  MsgBeg:='Анализ адекватности модели невозможен:'+#13;
  MsgUserAbort:='Операция прервана пользователем.';
  if((dsAVector=dsUndeterminate)or(dsR=dsUndeterminate)) then
  begin
    // Средних нет - запрос на проведение анализа
    Resp:=AskYNC('Вектор средних значений наблюдений'+#13+
      'не определен. Вычислить его сейчас - ?');
    case Resp of
      idYes:
      begin
        CohrenAnalysis;
        if((dsAvector=dsUndeterminate)or(dsR=dsUndeterminate)) then
        begin
          Warning(MsgBeg+'Вектор средних значений не определен');
          exit;
        end;
      end;
      idNo:
      begin
        Warning(MsgBeg+MsgUserAbort);
        exit;
      end;
      idCancel:
        exit;
    end;
  end; // if dsAVector=dsUndeterminate
  // Средние есть.
  if(dsMainMatrix=dsUndeterminate) then
  begin
    // Первичная матрица не определена - жалуемся и на выход
    Warning(MsgBeg+'не определена кодовая матрица плана.');
    exit;
  end;
  // Если коэффициенты модели не рассчитаны, рассчитываем их сейчас.
  if((dsBVector=dsUndeterminate)or(dsActualBSize=dsUndeterminate)) then
  begin
    Resp:=AskYNC('Коэффициенты модели не известны'+#13+
      'Определить их сейчас - ?');
    case Resp of
      idNo:
      begin
        Warning(MsgBeg+MsgUserAbort);
        exit;
      end;
      idCancel:
        exit;
      idYes:
        StudentAnalysis;
    end;
    if((dsBVector=dsUndeterminate)or(dsActualBSize=dsUndeterminate)) then
      exit;
  end;
  // Все, данные готовы. Можно начинать.
  // Если степеней свободы не остается - на выход.
  if(N<ActualBSize) then
  begin
    Warning(MsgBeg+'Число параметров модели превышает число опытов.');
    exit;
  end;
  // Если 0 степеней свободы или определена контрольная точка, то
  // производим анализ по контрольной точке.
  if((N=ActualBSize)or(dsCPoint<>dsUndeterminate)) then
  begin
    if(dsCPoint=dsUndeterminate) then
    begin
      if(pType=ptFromWizard) then
      begin
        if(Model=nil) then
          nb:=Addens.Count
        else
          nb:=Model^.AddenCount;
      end
      else
        nb:=-1;
      if(N=nb) then
        Warning(MsgBeg+'Используется насыщенный план эксперимента.'+#13+
          'Для анализа адекватности требуется контрольная точка.')
      else
        Warning(MsgBeg+'Модель не имеет степеней свободы.'+#13+
          'Для анализа адекватности требуется контрольная точка.');
      CheckPoint;
      exit;
    end;
    // Рассчитываем дисперсию предсказания
    ECP.Create(CMatrix.nRows);
    DCP.Create(CMatrix.nRows);
    ExpandCPoint(ECP,CPoint);
    CMatrix.VectorMul(ECP,DCP);
    Dfore:=ECP.DotProduct(DCP);
    ECP.Destroy;
    DCP.Destroy;
    dsDfore:=dsValid;
    // Рассчитываем среднее в контрольной точке
    Ycp:=0;
    for i:=0 to Rcp-1 do
      Ycp:=Ycp+CPY.Elements^[i];
    Ycp:=Ycp/Rcp;
    // Вычисляем значение критериев Стьюдента
    if((Dfore<-1.0)or(sqrt(DV*(1+Dfore))=0.0)) then
      GetStudent(1,ST_R)
    else
      ST_R:=abs(Ycp-GetYC(CPoint))*sqrt(Rcp)/(sqrt(DV*(1+Dfore)));
    dsST_R:=dsValid;
    GetStudent(N*(R-1),ST_T);
    dsST_T:=dsValid;
    // Показываем результаты (+ 25.01.00)
    if(AppSetup.GeneralFlags.FisherResultsShow and (R>1)) then
    begin
      CheckCompleteForm.EDfore.Text:=FloatToStr(Dfore);
      CheckCompleteForm.EST_R.Text:=FloatToStr(ST_R);
      CheckCompleteForm.EST_T.Text:=FloatToStr(ST_T);
      if(ST_R>ST_T) then
        CheckCompleteForm.EResult.Text:='Неадекватна системе.'
      else
        CheckCompleteForm.EResult.Text:='Адекватна системе.';
      CheckCompleteForm.ShowModal;
    end;
    if(AppSetup.GeneralFlags.FisherActivateReport or (R=1)) then
    begin
      CreateReport;
      ShowReport;
    end;
  end;
  // Если степеней свободы > 0 то производим анализ по критерию Фишера.
  if(N>ActualBSize) then
  begin
    // Рассчитываем дисперсию адекватности.
    DYVector.Recreate(N);
    Pc.Create(Degree);
    Dad:=0;
    for i:=0 to MainMatrix.nRows-1 do
    begin
      MainMatrix.RowToVector(Pc,i,0);
      DYVector.Elements^[i]:=AVector.Elements^[i]-GetYC(Pc);
      DYVector.Elements^[i]:=DYVector.Elements^[i]*DYVector.Elements^[i];
      Dad:=Dad+DYVector.Elements^[i];
    end;
    Pc.Destroy;
    dsDYVector:=dsValid;
    Dad:=R*Dad/(N-ActualBSize);
    dsDad:=dsValid;
    GetFisher(N-ActualBSize,N*(R-1),FS_T);
    if(DV<>0.0) then
      FS_R:=Dad/DV
    else
    begin
      if(Dad=0.0) then
        FS_R:=0
      else
        FS_R:=1e+308;
    end;
    dsFS_T:=dsValid;
    dsFS_R:=dsValid;
    if(AppSetup.GeneralFlags.FisherResultsShow and (R>1)) then
    begin
      FisherCompleteForm.EDad.Text:=FloatToStr(Dad);
      FisherCompleteForm.EFS_R.Text:=FloatToStr(FS_R);
      FisherCompleteForm.EFS_T.Text:=FloatToStr(FS_T);
      if(FS_R>FS_T) then
        FisherCompleteForm.EResult.Text:='Неадекватна системе.'
      else
        FisherCompleteForm.EResult.Text:='Адекватна системе.';
      FisherCompleteForm.ShowModal;
    end;
    if(AppSetup.GeneralFlags.FisherActivateReport {or (R=1)}) then
    begin
      CreateReport;
      ShowReport;
    end;
  end;
  Result:=Success(acAnalysis);
  MainForm.StateChanged;
end;

// =======================================================================
// CreateCodeMatrix
// =======================================================================

procedure TMathPlan.CreateCodeMatrix;
var
 i,j,k,t  : integer;
 codef    : double;
begin
  MainMatrix.Recreate(N,Degree);
  dsMainMatrix:=dsValid;
  if(pType=ptFromWizard) then
  begin
    if(Gen=nil) then
    begin
      MainMatrix.Zero;
      Exit;
    end;
    Gen^.RequiredDimensions(i,j);
    Assert((i=N)and(j=Degree));
    {if((i<>N)or(j<>Degree)) then
    begin
      Warning(Format('Генератор плана "%s"'+#13+
        'требует размер матрицы: %dX%d, имеющийся: %dX%d.',
        [Gen^.Description,i,j,N,Degree]));
      dsMainMatrix:=dsUndeterminate;
      Result:=False;
      exit;
    end;}
    Gen^.Generate(MainMatrix.Elements);
    exit;
  end;
  // Создаем 2^k строк ядра плана
  k:=1;
  codef:=-1;
  for j:=0 to Degree-1 do
  begin
    i:=0;
    while(i<(1 shl Degree)) do
    begin
      for t:=0 to k-1 do MainMatrix.SetElement(i+t,j,codef);
      codef:=-codef;
      i:=i+k;
    end;
    k:=2*k;
  end;
  if(pType=ptFFE) then exit;
  // Если план композиционный, то дополняем его звездными точками
  if(pType=ptCCR) then
  begin
    // alpha := 2 ^ (k/4) - ротатабельный план
    Alpha:=1 shl Degree;
    Alpha:=sqrt(sqrt(alpha));
  end
  else
  begin
    // alpha := (( (2^k * N)^.5 - 2^k)/2)^.5 - ортогональный план
    Alpha:=sqrt(( sqrt((1 shl Degree)*N) - (1 shl Degree) )/2);
  end;
  dsAlpha:=dsValid;
  t:=0;
  i:=1 shl Degree;
  while(i<(N-1)) do
  begin
    j:=0;
    while(j<t) do
    begin
      MainMatrix.SetElement(i,j,0);
      MainMatrix.SetElement(i+1,j,0);
      j:=j+1;
    end;
    MainMatrix.SetElement(i,t,-Alpha);
    MainMatrix.SetElement(i+1,t,Alpha);
    j:=t+1;
    while(j<Degree) do
    begin
      MainMatrix.SetElement(i,j,0);
      MainMatrix.SetElement(i+1,j,0);
      j:=j+1;
    end;
    // Звездная точка - вследующем столбце
    t:=t+1;
    // Шаг - две строки
    i:=i+2;
  end;
  // Последняя точка - опыт в центре плана
  for j:=0 to Degree-1 do
    MainMatrix.SetElement(N-1,j,0);
end;

// =======================================================================
// ExpandCodeMatrix
// =======================================================================

function TMathPlan.ExpandCodeMatrix;
var
  i,j : integer;
  v : TVector;
  res : double;
begin
  dsExpandedMatrix:=dsUndeterminate;
  Result:=pType in [ptFFE..ptFromWizard];
  if(pType<>ptFromWizard) then
  begin
    ExpandCodeMatrixDefault;
    exit;
  end;
  // Элемент строки i столбца j - значение j-той функции в i-той точке.
  v.Create(MainMatrix.nCols);
  if(Model<>nil) then
  begin
    if((ExpandedMatrix.nRows<>N)or
       (ExpandedMatrix.nCols<>Model^.AddenCount)) then
       ExpandedMatrix.Recreate(N,Model^.AddenCount);
    for i:=0 to N-1 do
    begin
      for j:=0 to Model^.AddenCount-1 do
      begin
        // i-тая точка
        MainMatrix.RowToVector(v,i,0);
        // j-тая функция
        Model^.Addens(j)^.Value(v.Elements,Res);
        ExpandedMatrix.SetElement(i,j,Res);
      end;
    end;
  end
  else // Model=nil
  begin
    if(Addens.Count<1) then
    begin
      Warning('ExpandCodeMatrix: Addens.Count<1');
      exit;
    end;
    if((ExpandedMatrix.nRows<>N)or
       (ExpandedMatrix.nCols<>Addens.Count)) then
       ExpandedMatrix.Recreate(N,Addens.Count);
    for i:=0 to N-1 do
    begin
      for j:=0 to Addens.Count-1 do
      begin
        // i-тая точка
        MainMatrix.RowToVector(v,i,0);
        // j-тая функция
        PAdden(Addens[j])^.Value(v.Elements,Res);
        ExpandedMatrix.SetElement(i,j,Res);
      end;
    end;
  end;
  dsExpandedMatrix:=dsValid;
  v.Destroy;
end;

// =======================================================================
// ExpandCodeMatrixDefault
// =======================================================================

procedure TMathPlan.ExpandCodeMatrixDefault;
var
  i,j,l,m : integer;
  Nffe : integer;
  Lexp : integer;
begin
  // Тип плана определяется по количетсву факторов и количеству опытов.
  Nffe:=1 shl Degree;
  if(N=Nffe) then
  begin
    // План полного факторного эксперимента.
    // Расширенная матрица содержит дополнительный столбец из единиц.
    Lexp:=Degree+1;
    if((ExpandedMatrix.nRows<>N)or
       (ExpandedMatrix.nCols<>Lexp)) then
      ExpandedMatrix.Recreate(N,Lexp);
    ExpandedMatrix.SetSubMat(MainMatrix,0,1);
    for i:=0 to MainMatrix.nRows-1 do ExpandedMatrix.SetElement(i,0,1);
    dsExpandedMatrix:=dsValid;
    exit;
  end;
  Assert(N=(Nffe + 2*Degree + 1));
  // Расширенная матрица содержит:
  //  - столбец из единиц
  //  - k столбцов исходной матрицы
  //  - k(k-1)/2 столбцов из произведений разных столбцов
  //  - k столбцов из произведений одинаковых столбцов
  //  всего - 1+2*k+(k^2-k)/2 столбцов.
  Lexp:=1+2*Degree+(Degree*(Degree-1) div 2);
  if((ExpandedMatrix.nRows<>N)or(ExpandedMatrix.nCols<>Lexp)) then
    ExpandedMatrix.ReCreate(N,Lexp);
  ExpandedMatrix.SetSubMat(MainMatrix,0,1);
  for i:=0 to N-1 do ExpandedMatrix.SetElement(i,0,1);
  // Номер следующего столбца = k+2 (считая с единицы)
  j:=Degree+1;
  // Cочетания l-го m-го столбцов удобно вычислять во вложенном цикле
  for l:=0 to Degree-2 do
    for m:=l+1 to Degree-1 do
    begin
      // Цикл по элементам j-го столбца результата
      for i:=0 to MainMatrix.nRows-1 do
        ExpandedMatrix.SetElement(i,j,
          MainMatrix.GetElement(i,l)*MainMatrix.GetElement(i,m));
      // Следующий столбец
      j:=j+1;
    end;
  // Остались k столбцов из квадратов элементов одинаковых столбцов.
  l:=0;
  for j:=ExpandedMatrix.nCols-Degree to ExpandedMatrix.nCols-1 do
  begin
    for i:=0 to ExpandedMatrix.nRows-1 do
      ExpandedMatrix.SetElement(i,j,
        MainMatrix.GetElement(i,l)*MainMatrix.GetElement(i,l));
    l:=l+1;
  end;
  dsExpandedMatrix:=dsValid;
  exit;
end;

// =======================================================================
// ExpandCPoint
// =======================================================================

procedure TMathPlan.ExpandCPoint;
var
  j   : integer;
  len : integer;
  res : double;
  cnt : integer;
begin
  Assert(AddenUsed <> nil);	// 10.08.2000
  if(pType<>ptFromWizard) then
  begin
    ExpandCPointDefault(Dst,Src);
    exit;
  end;
  if(Model<>nil) then
  begin
  	len:=Model^.AddenCount-1;
    cnt:=0;
    for j:=0 to len do
    begin
        if Boolean(AddenUsed[j]) then
        begin
    		Model^.Addens(j)^.Value(Src.Elements,Res);
    		Dst.SetElement(cnt,Res);
        	cnt:=cnt+1;
        end;
    end;
  end
  else // Model=nil
  begin
  	len:=Addens.Count-1;
    cnt:=0;
    for j:=0 to len do
    begin
        if Boolean(AddenUsed[j]) then
        begin
      		PAdden(Addens[j])^.Value(Src.Elements,Res);
      		Dst.SetElement(cnt,Res);
            cnt:=cnt+1;
        end;
    end;
  end;
end;

// =======================================================================
// ExpandCPointDefault
// =======================================================================

procedure TMathPlan.ExpandCPointDefault;
var
  j,l,m : integer;
  Nffe : integer;
  cnt : integer;
begin
  // Тип плана определяется по количетсву факторов и количеству опытов.
  Nffe:=1 shl Degree;
  cnt:=0;
  if(N=Nffe) then
  begin
  	if Boolean(AddenUsed[0]) then
    begin
    	Dst.SetElement(0,1.0);
        cnt:=1;
    end;
    for j:=0 to Src.nSize-1 do
    	if Boolean(AddenUsed[j+1]) then
        begin
      		Dst.SetElement(cnt,Src.GetElement(j));
            cnt:=cnt+1;
        end;
    exit;
  end;
  Assert(N=(Nffe + 2*Degree + 1));
  	if Boolean(AddenUsed[0]) then
    begin
    	Dst.SetElement(0,1.0);
        cnt:=1;
    end;
  for j:=0 to Src.nSize-1 do
  	if Boolean(AddenUsed[j+1]) then
    begin
    	Dst.SetElement(cnt,Src.GetElement(j));
        cnt:=cnt+1;
    end;
  j:=Src.nSize;
  // Cочетания l-го m-го столбцов удобно вычислять во вложенном цикле
  for l:=0 to Degree-2 do
  begin
    for m:=l+1 to Degree-1 do
    begin
    	if Boolean(AddenUsed[j]) then
        begin
      		Dst.SetElement(cnt,Src.GetElement(l)*Src.GetElement(m));
            cnt:=cnt+1;
        end;
		j:=j+1;
    end;
  end;
  // Остались k столбцов из квадратов элементов одинаковых столбцов.
  l:=0;
  for j:=Src.nSize-Degree to Src.nSize-1 do
  begin
    if Boolean(AddenUsed[j]) then
    begin
    	Dst.SetElement(cnt,Src.GetElement(l)*Src.GetElement(l));
        cnt:=cnt+1;
    end;
  	l:=l+1;
  end;
end;

// =======================================================================
// Создание матрицы в натуральном выражении на основе кодовой матрицы
// =======================================================================
procedure TMathPlan.NaturalFromCode;
var
  i,j : integer;
begin
  Assert((NaturalMatrix.nRows=MainMatrix.nRows) and
         (NaturalMatrix.nCols=MainMatrix.nCols));
  Assert(MLVector.nSize=DFVector.nSize);
  Assert(MLVector.nSize=MainMatrix.nCols);
  // Все проверки закончены. Собственно преобразование.
  for i:=0 to MainMatrix.nRows-1 do
  begin
    for j:=0 to MainMatrix.nCols-1 do
    begin
      NaturalMatrix.Elements^[i]^[j]:= MLVector.Elements^[j] +
        MainMatrix.Elements^[i]^[j] * DFVector.Elements^[j];
    end;
  end;
end;

// =======================================================================
// Нахождение точки кодового пространства по точке факторного
// =======================================================================
procedure TMathPlan.NtoC;
var
  i : integer;
begin
  for i:=0 to Degree-1 do
  begin
    if(DFVector.Elements^[i]<>0.0) then
      Dst.Elements^[i]:=(Src.Elements^[i]-MLVector.Elements^[i])/
        DFVector.Elements^[i]
    else
      Dst.Elements^[i]:=Src.Elements^[i]-MLVector.Elements^[i];
  end;
end;

// =======================================================================
// Нахождение точки факторного пространства по точке кодового
// =======================================================================
procedure TMathPlan.CtoN;
var
  i : integer;
begin
  for i:=0 to Degree-1 do
  begin
    Dst.Elements^[i]:=Src.Elements^[i]*DFVector.Elements^[i]
      +MLVector.Elements^[i];
  end;
end;

// =======================================================================
// GetKNR
// =======================================================================

procedure TMathPlan.GetKNR;
begin
  if(K<>nil) then K^:=Degree;
  if(N<>nil) then N^:=Self.N;
  if(R<>nil) then R^:=Self.R;
end;

// =======================================================================
// NaturalToCode
// =======================================================================

procedure TMathPlan.NaturalToCode(Dst,Src:PDoubleArray);
var
  Fiction : array[0..1] of TVector;
begin
  if((Dst<>nil)and(Src<>nil)) then
  begin
    Fiction[0].nSize:=Degree;
    Fiction[1].nSize:=Degree;
    Fiction[0].Elements:=Src;
    Fiction[1].Elements:=Dst;
    NtoC(Fiction[1],Fiction[0]);
  end;
end;

// =======================================================================
// CodeToNatural
// =======================================================================

procedure TMathPlan.CodeToNatural;
var
  Fiction : array[0..1] of TVector;
begin
  if((Dst<>nil)and(Src<>nil)) then
  begin
    Fiction[0].nSize:=Degree;
    Fiction[1].nSize:=Degree;
    Fiction[0].Elements:=Src;
    Fiction[1].Elements:=Dst;
    CtoN(Fiction[1],Fiction[0]);
  end;
end;

// =======================================================================
// Report (last modified: 25.01.00)
// =======================================================================
procedure TMathPlan.Report;
var
  str : array[0..255] of byte;
  i : integer;
  rf : PReportFlags;
begin
  rf:=@AppSetup.ReportFlags;
  // Устанавливаем строку - разделитель
  if(rf^.UseDivider) then
    SetDivider(rf^.DividerString)
  else
    SetDivider('');
  // ---------------------------------------------------------------------
  if(rf^.TimeDate) then
  begin
    Windows.GetTimeFormat(LOCALE_SYSTEM_DEFAULT,0,
      nil,nil,PChar(@str),sizeof(str));
    fPrint(Stream,'Отчет составлен: '+PChar(@str)+', ');
    Windows.GetDateFormat(LOCALE_SYSTEM_DEFAULT,DATE_SHORTDATE,
      nil,nil,PChar(@str),sizeof(str));
    fPrint(Stream,PChar(@str)+'.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.Degree) then
  begin
    if(dsDegree=dsUndeterminate) then
      fPrint(Stream,'Количество факторов плана не определено.'+#13)
    else
      fPrint(Stream,'Количество факторов: '+IntToStr(Degree)+#13);
  end;
  // ---------------------------------------------------------------------
  if(rf^.pType) then
  begin
    if(dspType=dsUndeterminate) then
      fPrint(Stream,'Тип плана не определен.'+#13)
    else
    begin
      fPrint(Stream,'Тип плана: ');
      case pType of
        ptFFE:
          fPrint(Stream,
            'встроенный план полного факторного эксперимента.'+#13);
        ptCCO:
          fPrint(Stream,
            'встроенный центральный композиционный ортогональный.'+#13);
        ptCCR:
          fPrint(Stream,
            'встроенный центральный композиционный ротатабельный.'+#13);
        ptFromWizard:
        begin
          if(Gen<>nil) then
            fPrint(Stream,Gen^.Description+#13)
          else
            fPrint(Stream,
              'План общего вида (определен пользователем).'+#13);
        end;
        else
          fPrint(Stream,'План общего вида ('+IntToStr(Ord(pType))
            +')'+#13);
      end;
    end;
  end;
  // ---------------------------------------------------------------------
  if(rf^.N) then
  begin
    fPrint(Stream,'Полное количество экспериментов: ');
    if(dsN=dsUndeterminate) then
    begin
      fPrint(Stream,'не определено.'+#13);
    end;
    fPrint(Stream,IntToStr(N)+#13);
  end;
  if(rf^.pType or rf^.N) then
    Divider(Stream);
  // ---------------------------------------------------------------------
  if(rf^.ModelType) then
  begin
    fPrint(Stream,'Тип модели: ');
    if(pType=ptFFE) then
    begin
      if(dsN<>dsUndeterminate) then
        fPrint(Stream,'Полином первого порядка от '+IntToStr(Degree)
          +' переменных.'+#13)
      else
        fPrint(Stream,'Полином первого порядка.'+#13);
    end
    else
    begin
      if(pType<>ptFromWizard) then
      begin
        if(dsN<>dsUndeterminate) then
          fPrint(Stream,
            'Полином второго порядка от '+IntToStr(Degree)+
            ' переменных.'+#13)
        else
          fPrint(Stream,'Полином второго порядка.'+#13);
      end
      else
      begin
        if(Model<>nil) then
          fPrint(Stream,Model^.Description+#13)
        else
        begin
          fPrint(Stream,'Модель общего вида (определена пользователем).'
            +#13);
          if(Addens.Count<>0) then
          begin
            fPrint(Stream,'Слагаемые: ');
            for i:=0 to Addens.Count-2 do
              fPrint(Stream,PAdden(Addens[i])^.Description+';');
            fPrint(Stream,
              PAdden(Addens[Addens.Count-1])^.Description+'.'+#13);
          end;
        end;
      end;
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.MainMatrix) then
  begin
    if(dsMainMatrix=dsUndeterminate) then
    begin
      fPrint(Stream,
        'Матрица плана в кодовом выражении не определена.'+#13);
    end
    else
    begin
      fPrint(Stream,'Матрица плана в кодовом выражении:'+#13);
      MainMatrix.StreamDisplay(Stream);
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.MLVector) then
  begin
    if(dsMLVector=dsUndeterminate) then
    begin
      fPrint(Stream,'Вектор основных уровней факторов не определен.'+#13);
    end
    else
    begin
      fPrint(Stream,'Вектор основных уровней факторов:'+#13);
      MLVector.StreamDisplay(Stream);
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.DFVector) then
  begin
    if(dsDFVector=dsUndeterminate) then
    begin
      fPrint(Stream,'Вектор интервалов варьирования не определен.'+#13);
    end
    else
    begin
      fPrint(Stream,'Вектор интервалов варьирования:'+#13);
      DFVector.StreamDisplay(Stream);
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.NaturalMatrix) then
  begin
    if(dsNaturalMatrix=dsUndeterminate) then
    begin
      fPrint(Stream,
        'Матрица плана в натуральном выражении не определена.'+#13);
    end
    else
    begin
      NaturalFromCode;
      fPrint(Stream,'Матрица плана в натуральном выражении:'+#13);
      NaturalMatrix.StreamDisplay(Stream);
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.ExpandedMatrix) then
  begin
    if(dsExpandedMatrix<>dsUndeterminate) then
    begin
      fPrint(Stream,'Матрица базисных функций:'+#13);
      ExpandedMatrix.StreamDisplay(Stream);
    end
    else
    begin
      fPrint(Stream,'Матрица базисных функций не создана.'+#13);
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.MMatrix) then
  begin
    if(dsMMatrix<>dsUndeterminate) then
    begin
      fPrint(Stream,'Информационная матрица: '+#13);
      MMatrix.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Информационная матрица не создана.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.CMatrix) then
  begin
    if(dsCMatrix<>dsUndeterminate) then
    begin
      fPrint(Stream,'Ковариационная матрица: '+#13);
      CMatrix.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Ковариационная матрица не создана.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.R) then
  begin
    if(dsR<>dsUndeterminate) then
      fPrint(Stream,'Число повторений каждого эксперимента: '+
        IntToStr(R)+#13)
    else
      fPrint(Stream,'Число повторений экспериментов не определено.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.YMatrix) then
  begin
    if(dsYMatrix<>dsUndeterminate) then
    begin
      fPrint(Stream,'Матрица откликов: '+#13);
      YMatrix.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Матрица откликов не определена.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.AVector) then
  begin
    if(dsAVector<>dsUndeterminate) then
    begin
      fPrint(Stream,'Вектор средних значений наблюдений: '+#13);
      AVector.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Вектор средних значений наблюдений не найден.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(R=1) then begin
    fPrint(Stream,'Статистический анализ данных невозможен: опыты не повторялись.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.DVector and (R>1)) then
  begin
    if(dsDVector<>dsUndeterminate) then
    begin
      fPrint(Stream,'Вектор дисперсий наблюдений: '+#13);
      DVector.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Вектор дисперсий наблюдений не найден.'+#13);
  end;
  // ---------------------------------------------------------------------
  if(rf^.SVector and (R>1)) then
  begin
    if(dsSVector<>dsUndeterminate) then
    begin
      fPrint(Stream,'Вектор ошибок наблюдений: '+#13);
      SVector.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Вектор ошибок наблюдений не найден.'+#13);
  end;
  // ---------------------------------------------------------------------
  if(rf^.DV and (R>1)) then
  begin
    if(dsDV<>dsUndeterminate) then
      fPrint(Stream,'Дисперсия воспроизводимости: '+Format('%.5g',[DV])
        +#13)
    else
      fPrint(Stream,'Дисперсия воспроизводимости не определена.');
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.Dmax and (R>1)) then
  begin
    if(dsDmax<>dsUndeterminate) then
      fPrint(Stream,'Максимальная из дисперсий опытов: '+
        Format('%.5g',[Dmax])+#13)
    else
      fPrint(Stream,'Максимальная из дисперсий опытов не найдена.');
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.ActualBSize and (R>1)) then
  begin
    if(dsActualBSize<>dsUndeterminate) then
      fPrint(Stream,'Число значимых параметров: '+
        Format('%d',[ActualBSize])+#13)
    else
      fPrint(Stream,'Число значимых параметров не найдено.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.BVector) then
  begin
    if(dsBVector <> dsUndeterminate) then
    begin
      (*
       * 25 jan 2012
      if(dsBNames=dsUndeterminate) then
        *)
      FillBNames;
      fPrint(Stream,'Вектор параметров модели: '+#13);
      for i:=0 to BNames.Count-1 do
        fPrint(Stream,BNames.Names[i]+#9+BNames.Values[BNames.Names[i]]+#13);
    end
    else
      fPrint(Stream,'Вектор параметров модели не найден.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.Dad and (R>1)) then
  begin
    if(dsDad<>dsUndeterminate) then
      fPrint(Stream,'Дисперсия адекватности: '+Format('%.5g',[Dad])+#13)
    else
      fPrint(Stream,'Дисперсия адекватности не найдена.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.CohrenAnalysis and (R>1)) then
  begin
    if((dsCH_R<>dsUndeterminate)and(dsCH_T<>dsUndeterminate)) then
    begin
      fPrint(Stream,'Результаты анализа однородности дисперсий: '+#13);
      fPrint(Stream,'Расчетное значение критерия Кохрена: '+
        Format('%.5g',[CH_R])+#13);
      fPrint(Stream,'Табличное значение критерия Кохрена: '+
        Format('%.5g',[CH_T])+#13);
      if(CH_R>CH_T) then
        fPrint(Stream,'Дисперсии параллельных опытов неоднородны.'+#13)
      else
        fPrint(Stream,'Дисперсии параллельных опытов однородны.'+#13);
    end
    else
      fPrint(Stream,'Анализ однородности не произведен.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.FisherAnalysis and (R>1)) then
  begin
    if((dsFS_R<>dsUndeterminate)and(dsFS_T<>dsUndeterminate)) then
    begin
      fPrint(Stream,
        'Результаты анализа адекватности по критерию Фишера: '+#13);
      fPrint(Stream,'Расчетное значение критерия Фишера: '+
        Format('%.5g',[FS_R])+#13);
      fPrint(Stream,'Табличное значение критерия Фишера: '+
        Format('%.5g',[FS_T])+#13);
      if(FS_R>FS_T) then
        fPrint(Stream,
          'Выбранная модель неадекватна исследуемой системе.'+#13)
      else
        fPrint(Stream,
          'Выбранная модель адекватно описывает исследуемую систему.'+#13);
    end
    else
    begin
      fPrint(Stream,
        'Анализ адекватности по критерию Фишера не производился.'+#13);
    end;
    if((dsST_R<>dsUndeterminate)and(dsST_T<>dsUndeterminate)) then
    begin
      fPrint(Stream,
        'Результаты анализа адекватности по контрольной точке: '+#13);
      fPrint(Stream,'Расчетное значение критерия Стьюдента: '+
        Format('%.5g',[ST_R])+#13);
      fPrint(Stream,'Табличное значение критерия Стьюдента: '+
        Format('%.5g',[ST_T])+#13);
      if(ST_R>ST_T) then
        fPrint(Stream,
          'Выбранная модель неадекватна исследуемой системе.'+#13)
      else
        fPrint(Stream,
          'Выбранная модель адекватно описывает исследуемую систему.'+#13);
    end
    else
    begin
      fPrint(Stream,
        'Анализ адекватности по контрольной точке не производился.'+#13);
    end;
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.MainPoint) then
  begin
    if(dsMainPoint<>dsUndeterminate) then
    begin
      fPrint(Stream,'Последний найденный экстремум: '+#13);
      MainPoint.StreamDisplay(Stream);
    end
    else
      fPrint(Stream,'Поиск экстремума не производился.'+#13);
    Divider(Stream);
  end;
  // ---------------------------------------------------------------------
  if(rf^.MarkEndOfReport) then
    fPrint(Stream,'<конец отчета>'+#13);
end;

function TMathPlan.GetStateString;
begin
  Result:=PChar(StatusString);
end;

function TMathPlan.FileName;
begin
  Result:=PChar(CurrentFile);
end;

function TMathPlan.GetGServer:pIGraphServer;
begin
  Result:=pIGServer;
end;

procedure TMathPlan.TemporaryClose;
begin
  g:=(pIGServer<>nil)and(pIGServer^.CanDo(acHide));
  if(g) then pIGServer^.Show(False);
  c:=CMForm<>nil;
  n:=NMForm<>nil;
  m:=MLForm<>nil;
  d:=DFForm<>nil;
  y:=YForm<>nil;
  r:=ReportForm<>nil;
  DestroyForms;
end;

procedure TMathPlan.RestoreVisibleForms;
begin
  if((g)and(pIGServer<>nil)and(pIGServer^.CanDo(acShow))) then
    pIGServer^.Show(True);
  if(c) then EditCodeMatrix(True);
  if(n) then EditNaturalMatrix(True);
  if(m) then EditMLVector(True);
  if(d) then EditDFVector(True);
  if(y) then EditYMatrix(True);
  if(r) then
  begin
    CreateReportForm;
    CreateReport;
  end;
end;

procedure TMathPlan.LBFillWithNames(List:TStrings);
begin
  List.Clear;
  List.AddStrings(KNames);
end;
// =======================================================================
// TMathPlanData
// =======================================================================

const PLAN_MAGIC : DWORD = $4C50544D; // 'MTPL'

constructor TMathPlanData.Create;
begin
  DataState:=dsUndeterminate;
  GenDesc:='';
  ModelDesc:='';
  PlanMatrix.Create(0,0);
  YMatrix.Create(0,0);
  MLVector.Create(0);
  DFVector.Create(0);
  CPointPresent:=False;
  CPoint.Create(0);
  CPY.Create(0);
  AddensDesc:=TStringList.Create;
  AddensDesc.Clear;
  KNames:=TStringList.Create;
  KNames.Clear;
end;

destructor TMathPlanData.Destroy;
begin
  DFVector.Destroy;
  MLVector.Destroy;
  YMatrix.Destroy;
  PlanMatrix.Destroy;
  CPoint.Destroy;
  CPY.Destroy;
  AddensDesc.Free;
  KNames.Free;
end;

function TMathPlanData.MakeCopy;
var
  i : integer;
const
  NoMem : string =
    'Недостаточно памяти для сохранения плана.'+#13+
    'Попробуйте сохранить его по частям через'+#13+
    'меню "Экспорт". Извините.';
begin
  Result:=False;
//  LastErrorStr:=GenericErrStr;
  DataState:=dsUndeterminate;
  // Данные, которые полностью определяют план:
  // - тип плана
  // - кодовая матрица либо натуральная марица - в зависимости от
  //   их приоритета (флаг CodeFirst); количество факторв и количество
  //   опытов определяются указаннами матрицами
  // - вектор основных уровней
  // - вектор интервалов варьирования
  // - матрица наблюдений
  // - дополнительно - описания генератора, модели, слагаемых.

  // Провераем, есть ли требуемые данные.
  Assert(LivingObject.dspType=dsValid);
  if(not LivingObject.CanDo(acExportCode)) then
  begin
    Warning('Не определена кодовая матрица.');
    exit;
  end;
  if(not LivingObject.CanDo(acExportML)) then
  begin
    Warning('Не определен вектор основных уровней.');
    exit;
  end;
  if(not LivingObject.CanDo(acExportDF)) then
  begin
    Warning('Не определен вектор интервалов варьирования.');
    exit;
  end;
  if(not LivingObject.CanDo(acExportY)) then
  begin
    Warning('Не определена матрица наблюдений.');
    exit;
  end;
  if((LivingObject.pType=ptFromWizard)and
     (LivingObject.Model=nil)and
     (LivingObject.Addens.Count<1)) then
  begin
    Warning('Не заданы слагаемые модели.');
    exit;
  end;
  // Копируем данные
  try
    pType:=LivingObject.pType;
    PlanMatrix.Recreate(LivingObject.MainMatrix.nRows,
      LivingObject.MainMatrix.nCols);
    PlanMatrix.CopyFrom(LivingObject.MainMatrix);
    if(LivingObject.dsCPoint<>dsUndeterminate) then
    begin
      CPointPresent:=True;
      CPoint.Recreate(LivingObject.CPoint.nSize);
      CPoint.CopyFrom(LivingObject.CPoint);
      CPY.Recreate(LivingObject.CPY.nSize);
      CPY.CopyFrom(LivingObject.CPY);
    end;
    MLVector.Recreate(LivingObject.MLVector.nSize);
    MLVector.CopyFrom(LivingObject.MLVector);
    DFVector.Recreate(LivingObject.DFVector.nSize);
    DFVector.CopyFrom(LivingObject.DFVector);
    YMatrix.Recreate(LivingObject.YMatrix.nRows,LivingObject.YMatrix.nCols);
    YMatrix.CopyFrom(LivingObject.YMatrix);
    if(pType=ptFromWizard) then
    begin
      if(LivingObject.Gen<>nil) then
        GenDesc:=LivingObject.Gen^.Description;
      if(LivingObject.Model<>nil) then
        ModelDesc:=LivingObject.Model^.Description
      else
      begin
        AddensDesc.Clear;
        if(LivingObject.Addens.Count>MaximumAddens) then
        begin
          Warning(
            'Число слагаемых слишком велико. Описания будут'+#13+
            'сохранены только для первых '+IntToStr(MaximumAddens)+
            ' - ти из них.');
          AddensDesc.Capacity:=MaximumAddens;
        end
        else
          AddensDesc.Capacity:=LivingObject.Addens.Count;
        for i:=0 to AddensDesc.Capacity-1 do
          AddensDesc.Add(PAdden(LivingObject.Addens[i])^.StdName);
      end;
    end;
    // Имена факторов.
    KNames.Clear;
    KNames.AddStrings(LivingObject.KNames);
    NameY:=LivingObject.NameY;
  except
    on EOutOfMemory do
    begin
      Error(NoMem);
      exit;
    end;
  end;
  // Копия создана.
  Result:=True;
  DataState:=dsValid;
end;

function TMathPlanData.SaveToStream;
var
  i : dword;
begin
  Result:=False;
  Assert(DataState<>dsUndeterminate);
  // Сохраняем основной блок данных.
  try
    i:=PLAN_MAGIC;
    Stream.WriteBuffer(i,sizeof(i));
    Stream.WriteBuffer(pType,sizeof(TPlanType));
    PlanMatrix.SaveToStream(Stream);
    Stream.WriteBuffer(CPointPresent,sizeof(CPointPresent));
    if(CPointPresent) then
    begin
      CPoint.SaveToStream(Stream);
      CPY.SaveToStream(Stream);
    end;
    MLVector.SaveToStream(Stream);
    DFVector.SaveToStream(Stream);
    YMatrix.SaveToStream(Stream);
  // Если план не является встроенным, то необходимо сохранение описаний
  // генератора и модели либо слагаемых.
    if(pType=ptFromWizard) then
    begin
      if(GenDesc='') then
      begin
        WriteStr(Stream,'NULL');
      end
      else
        WriteStr(Stream,GenDesc);
      if(ModelDesc='') then
      begin
        WriteStr(Stream,'NULL');
        i:=AddensDesc.Count;
        Stream.WriteBuffer(i,sizeof(i));
        for i:=0 to AddensDesc.Count-1 do
          WriteStr(Stream,AddensDesc[i]);
      end
      else
        WriteStr(Stream,ModelDesc);
    end;
    // Имена факторов.
    for i:=1 to KNames.Count do
      WriteStr(Stream,KNames[i-1]);
    WriteStr(Stream,NameY);
    Result:=True;
  except
    on EWriteError do
    begin
      Error('Ошибка записи. Извините.');
      exit;
    end;
  end;
end;

function TMathPlanData.LoadFromStream;
var
  i : dword;
const
  NoMem : string = 'Недостаточно памяти для открытия плана. Извините.';
begin
  Result:=False;
  try
    Stream.ReadBuffer(i,sizeof(i));
    if(i<>PLAN_MAGIC) then
    begin
      Error('Файл поврежден. Извините.');
      exit;
    end;
    // Читаем основной блок данных.
    Stream.ReadBuffer(pType,sizeof(TPlanType));
    if(not (pType in [ptFFE,ptCCO,ptCCR,ptFromWizard])) then
    begin
      Error('Файл содержит описание неизвестного плана. Извините.');
      exit;
    end;
    PlanMatrix.LoadFromStream(Stream);
    Stream.ReadBuffer(CPointPresent,sizeof(CPointPresent));
    if(CPointPresent) then
    begin
      CPoint.LoadFromStream(Stream);
      CPY.LoadFromStream(Stream);
    end;
    MLVector.LoadFromStream(Stream);
    DFVector.LoadFromStream(Stream);
    YMatrix.LoadFromStream(Stream);
    // Если план не является встроенным, то необходимо получить описания
    // генератора и модели либо слагаемых.
    if(pType=ptFromWizard) then
    begin
      GenDesc:=ReadStr(Stream);
      if(GenDesc='NULL') then GenDesc:='';
      ModelDesc:=ReadStr(Stream);
      if(ModelDesc='NULL') then
      begin
        ModelDesc:='';
        Stream.ReadBuffer(i,sizeof(i));
        if((i<0)or(i>MaximumAddens)) then
          Raise EReadError.Create('Неверное количество слагаемых.');
        AddensDesc.Capacity:=i;
        for i:=0 to AddensDesc.Capacity-1 do
          AddensDesc.Add(ReadStr(Stream));
      end;
    end;
    // Имена факторов.
    KNames.Capacity:=PlanMatrix.nCols;
    for i:=1 to KNames.Capacity do
      KNames.Add(ReadStr(Stream));
    NameY:=ReadStr(Stream);
    Result:=True;
    DataState:=dsValid;
  except
    on EReadError do
    begin
      Error('Ошибка чтения. Извините.');
      exit;
    end;
    on EOutOfMemory do
    begin
      Error(NoMem);
      exit;
    end;
  end;
end;

function TMathPlanData.EnliveObject;
var
  Ind,i: integer;
const
  NoMem : string = 'Недостаточно памяти для открытия плана. Извините.';
begin
  Result:=False;
  Assert(DataState<>dsUndeterminate);
  Assert(not DeadObject.CanDo(acCreateReport));
  DeadObject.CleanFields;
  if(pType=ptFromWizard) then
  begin
    if(GenDesc<>'') then
    begin
      Ind:=ExternalModules.GenByName(GenDesc);
      if(Ind=-1) then
      begin
        Warning('Описание плана "'+GenDesc+'"'+#13+
          'не найдено во внешних модулях.');
        exit;
      end;
      DeadObject.Gen:=ExternalModules.Gens[Ind];
    end
    else
      DeadObject.Gen:=nil;
    if(ModelDesc<>'') then
    begin
      Ind:=ExternalModules.ModelByName(ModelDesc);
      if(Ind=-1) then
      begin
        Warning('Описание модели "'+ModelDesc+'"'+#13+
          'не найдено во внешних модулях.');
        exit;
      end;
      DeadObject.Model:=ExternalModules.Models[Ind];
    end
    else
    begin
      DeadObject.Model:=nil;
      if(AddensDesc.Count=0) then
      begin
        Warning('Модель не содержит не одного слагаемого.');
        exit;
      end;
      DeadObject.Addens.Count:=AddensDesc.Count;
      for i:=0 to AddensDesc.Count-1 do
      begin
        Ind:=ExternalModules.AddenByStdName(AddensDesc[i]);
        if(Ind=-1) then
        begin
          Warning('Описание слагаемого "'+AddensDesc[i]+'"'+#13+
            'не найдено во внешних модулях.');
          exit;
        end;
        DeadObject.Addens[i]:=ExternalModules.Addens[Ind];
      end;
    end;
  end;
  DeadObject.pType:=pType;
  DeadObject.N:=PlanMatrix.nRows;
  DeadObject.SetDegree(PlanMatrix.nCols);
  try
    DeadObject.MainMatrix.Recreate(PlanMatrix.nRows,PlanMatrix.nCols);
    DeadObject.NaturalMatrix.Recreate(PlanMatrix.nRows,PlanMatrix.nCols);
    DeadObject.MainMatrix.CopyFrom(PlanMatrix);
    if(CPointPresent) then
    begin
      DeadObject.CPoint.Recreate(CPoint.nSize);
      DeadObject.CPY.Recreate(CPY.nSize);
      DeadObject.CPoint.CopyFrom(CPoint);
      DeadObject.CPY.CopyFrom(CPY);
      DeadObject.Rcp:=CPY.nSize;
      DeadObject.dsCPoint:=dsValid;
      DeadObject.dsCPY:=dsValid;
      DeadObject.dsRcp:=dsValid;
    end;
    DeadObject.MLVector.Recreate(MLVector.nSize);
    DeadObject.MLVector.CopyFrom(MLVector);
    DeadObject.DFVector.Recreate(DFVector.nSize);
    DeadObject.DFVector.CopyFrom(DFVector);
    DeadObject.YMatrix.Recreate(YMatrix.nRows,YMatrix.nCols);
    DeadObject.YMatrix.CopyFrom(YMatrix);
    DeadObject.R:=YMatrix.nCols;
    // Имена факторов.
    DeadObject.KNames.Clear;
    DeadObject.KNames.AddStrings(KNames);
    DeadObject.NameY:=NameY;
  except
    on EOutOfMemory do
    begin
      Error(NoMem);
      exit;
    end;
  end;
  DeadObject.dspType:=dsValid;
  DeadObject.dsN:=dsValid;
  DeadObject.dsDegree:=dsValid;
  DeadObject.dsMainMatrix:=dsValid;
  DeadObject.dsNaturalMatrix:=dsValid;
  DeadObject.dsMLVector:=dsValid;
  DeadObject.dsDFVector:=dsValid;
  DeadObject.dsYMatrix:=dsValid;
  DeadObject.dsR:=dsValid;
  DeadObject.dsKNames:=dsValid;
  DeadObject.SynchronizeMatrices(nil);
  DeadObject.ShowDataForms;
  Result:=True;
end;

end.

