//*****************************************************************************
// Copyright 2017-2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************

#pragma once

#include "ngraph/function.hpp"

namespace ngraph
{
    /// \brief Creates a clone of a function, with the shapes of that function's parameters
    ///        specialized to some more specific element types and shapes.
    /// \param f The function to be cloned.
    /// \param parameter_element_types The new parameter element types to substitute.
    /// \param parameter_shapes The new parameter shapes to substitute.
    /// \return A clone of f, with the parameter element types and shapes specialized.
    /// \throws CheckFailure if parameter_element_types or parameter_shapes is not valid
    ///         (see details).
    /// \throws NodeValidationError if node validation fails as the clone is being constructed.
    ///
    /// Creates a "shape-specialized" clone of an nGraph Function function.
    ///
    /// For example, suppose that a function f has three parameters with partial shapes:
    ///
    /// ```
    /// param0: ?
    /// param1: {1,?,3}
    /// param2: {?,?,4}
    /// ```
    ///
    /// Shape specialization would allow us to create a clone of f where the shapes are (for
    /// example):
    ///
    /// ```
    /// param0: {1,2}
    /// param1: {1,5,3}
    /// param2: {3,?,4}
    /// ```
    ///
    /// But not (for example):
    ///
    /// ```
    /// param1: {1,5,3,4}  // rank doesn't match {1,?,3}
    /// param1: {2,?,3}    // the "2" doesn't match the "1"
    /// param1: {?,?,3}    // the new shape is too relaxed: it doesn't require 1 for the first dim
    /// ```
    ///
    /// Note that validation errors can potentially occur during cloning. For example:
    ///
    /// ```
    /// n = Parameter{shape=?}
    /// m = Parameter{shape=?}
    /// x = n + m
    /// f = Function(x,{n,m})
    /// ```
    ///
    /// If we specialize n to the shape `{1,2,3}` and m to the shape `{4,5,6}`, cloning will fail
    /// because when we reconstruct the new x node, it will see that the shapes are inconsistent
    /// for elementwise add.
    ///
    /// Specialization of element types is also possible: `element::dynamic` can be specialized
    /// to a concrete element type or left dynamic; but a concrete element type can only be
    /// specialized to itself (e.g., specialization does not allow you to change `element::i32`
    /// to `element::i64`).
    ///
    /// It is required that:
    ///    1. The length of parameter_element_types and parameter_shapes is the same as the number
    ///       of f's parameters.
    ///    2. Each shape in parameter_shapes is a refinement of the shape of the corresponding
    ///       parameter of f. Roughly speaking, a shape s1 is said to "refine" s2 if s1 can be
    ///       obtained from s2 by filling in s2's question marks. See PartialShape::refines for
    ///       more details.
    ///    3. For all i, either the element type of fp_i is dynamic, or fp_i is the same as
    ///       parameter_element_types[i]. (Here fp_i is the ith parameter of f.)
    std::shared_ptr<Function>
        specialize_shapes(std::shared_ptr<Function> f,
                          const std::vector<element::Type>& parameter_element_types,
                          const std::vector<PartialShape>& parameter_shapes);
}
