include "primitives.alh"
include "modelling.alh"
include "object_operations.alh"

Composite schedule {
    {Contains} Success success {}
    {Contains} Atomic expand {
        LHS {
            Pre_Design/Block expand_lhs_0 {
                label = "0"
            }
            Pre_Design/IntegratorBlock expand_lhs_1 {
                label = "1"
            }
            Pre_Design/Block expand_lhs_2 {
                label = "2"
            }
            Pre_Design/Block expand_lhs_3 {
                label = "3"
            }
            Pre_Design/InitialCondition (expand_lhs_3, expand_lhs_1) {
                label = "4"
            }
            Pre_Design/Link (expand_lhs_0, expand_lhs_1) {
                label = "5"
            }
            Pre_Design/Link (expand_lhs_1, expand_lhs_2) {
                label = "6"
            }
        }
        RHS {
            Post_Design/Block expand_rhs_0 {
                label = "0"
            }
            Post_Design/IntegratorBlock expand_rhs_1 {
                label = "1"
            }
            Post_Design/Block expand_rhs_2 {
                label = "2"
            }
            Post_Design/Block expand_rhs_3 {
                label = "3"
            }
            Post_Design/InitialCondition (expand_rhs_3, expand_rhs_1) {
                label = "4"
            }
            Post_Design/Link (expand_rhs_0, expand_rhs_1) {
                label = "5"
            }
            Post_Design/ConstantBlock expand_rhs_7 {
                label = "7"
                value_value = $
                            Float function value(model : Element, name : String, mapping : Element):
                                return 0.01!
                        $
            }
            Post_Design/MultiplyBlock expand_rhs_8 {
                label = "8"
            }
            Post_Design/AdditionBlock expand_rhs_9 {
                label = "9"
            }
            Post_Design/DelayBlock expand_rhs_10 {
                label = "10"
                __id__ = $
                            String function new_id(model : Element, mapping : Element):
                                String result
                                result = cast_string(list_read(string_split(mapping["1"], "/"), 1)) + "_" + cast_string(list_read(string_split(mapping["2"], "/"), 1)) + "_D1"
                                log("Generate new delayblock with ID " + result)
                                return result!
                         $
            }
            Post_Design/DelayBlock expand_rhs_11 {
                label = "11"
                __id__ = $
                            String function new_id(model : Element, mapping : Element):
                                String result
                                result = cast_string(list_read(string_split(mapping["1"], "/"), 1)) + "_" + cast_string(list_read(string_split(mapping["2"], "/"), 1)) + "_D2"
                                log("Generate new delayblock with ID " + result)
                                return result!
                         $
            }
            Post_Design/ConstantBlock expand_rhs_12 {
                label = "12"
                value_value = $
                            Float function value(model : Element, name : String, mapping : Element):
                                return 0!
                        $
            }
            Post_Design/Link (expand_rhs_0, expand_rhs_10) {
                label = "13"
            }
            Post_Design/Link (expand_rhs_7, expand_rhs_8) {
                label = "14"
            }
            Post_Design/Link (expand_rhs_10, expand_rhs_8) {
                label = "15"
            }
            Post_Design/Link (expand_rhs_8, expand_rhs_9) {
                label = "16"
            }
            Post_Design/Link (expand_rhs_9, expand_rhs_2) {
                label = "17"
            }
            Post_Design/Link (expand_rhs_9, expand_rhs_11) {
                label = "18"
            }
            Post_Design/Link (expand_rhs_11, expand_rhs_9) {
                label = "19"
            }
            Post_Design/InitialCondition (expand_rhs_12, expand_rhs_10) {
                label = "20"
            }
            Post_Design/InitialCondition (expand_rhs_3, expand_rhs_11) {
                label = "21"
            }
        }
    }

    {Contains} ForAll optimize_inverse {
        LHS {
            Pre_Design/ConstantBlock opt_inv_lhs_0 {
                label = "0"
            }
            Pre_Design/InverseBlock opt_inv_lhs_1 {
                label = "1"
            }
            Pre_Design/Block opt_inv_lhs_2 {
                label = "2"
            }
            Pre_Design/Link (opt_inv_lhs_0, opt_inv_lhs_1) {
                label = "3"
            }
            Pre_Design/Link (opt_inv_lhs_1, opt_inv_lhs_2) {
                label = "4"
            }
        }
        RHS {
            Post_Design/ConstantBlock opt_inv_rhs_0 {
                label = "0"
            }
            Post_Design/InverseBlock opt_inv_rhs_1 {
                label = "1"
            }
            Post_Design/Block opt_inv_rhs_2 {
                label = "2"
            }
            Post_Design/Link (opt_inv_rhs_0, opt_inv_rhs_1) {
                label = "3"
            }
            Post_Design/ConstantBlock opt_inv_rhs_5 {
                label = "5"
                value_value = $
                            Float function value(model : Element, name : String, mapping : Element):
                                return float_division(1, read_attribute(model, mapping["0"], "value"))!
                        $
            }
            Post_Design/Link (opt_inv_rhs_5, opt_inv_rhs_2) {
                label = "6"
            }
        }
    }

    {Contains} ForAll optimize_negator {
        LHS {
            Pre_Design/ConstantBlock opt_neg_lhs_0 {
                label = "0"
            }
            Pre_Design/InverseBlock opt_neg_lhs_1 {
                label = "1"
            }
            Pre_Design/Block opt_neg_lhs_2 {
                label = "2"
            }
            Pre_Design/Link (opt_neg_lhs_0, opt_neg_lhs_1) {
                label = "3"
            }
            Pre_Design/Link (opt_neg_lhs_1, opt_neg_lhs_2) {
                label = "4"
            }
        }
        RHS {
            Post_Design/ConstantBlock opt_neg_rhs_0 {
                label = "0"
            }
            Post_Design/InverseBlock opt_neg_rhs_1 {
                label = "1"
            }
            Post_Design/Block opt_neg_rhs_2 {
                label = "2"
            }
            Post_Design/Link (opt_neg_rhs_0, opt_neg_rhs_1) {
                label = "3"
            }
            Post_Design/ConstantBlock opt_neg_rhs_5 {
                label = "5"
                value_value = $
                            Float function value(model : Element, name : String, mapping : Element):
                                return float_subtraction(0, read_attribute(model, mapping["0"], "value"))!
                        $
            }
            Post_Design/Link (opt_neg_rhs_5, opt_neg_rhs_2) {
                label = "6"
            }
        }
    }

    {Contains} ForAll remove_blocks {
        LHS {
            Pre_Design/Block rem_lhs_0 {
                label = "0"
                constraint = $
                                Boolean function constraint(model : Element, name : String):
                                    if (set_len(allOutgoingAssociationInstances(model, name, "Design/Link")) > 0):
                                        return False!
                                    elif (set_len(allOutgoingAssociationInstances(model, name, "Design/InitialCondition")) > 0):
                                        return False!
                                    else:
                                        return read_type(model, name) != "Design/ProbeBlock"!
                             $
            }
        }
        RHS {}
    }

    {Contains} ForAll optimize_adder {
        LHS {
            Pre_Design/ConstantBlock opt_add_lhs_0 {
                label = "0"
            }
            Pre_Design/ConstantBlock opt_add_lhs_1 {
                label = "1"
            }
            Pre_Design/AdditionBlock opt_add_lhs_2 {
                label = "2"
            }
            Pre_Design/Block opt_add_lhs_3 {
                label = "3"
            }
            Pre_Design/Link (opt_add_lhs_0, opt_add_lhs_2) {
                label = "4"
            }
            Pre_Design/Link (opt_add_lhs_1, opt_add_lhs_2) {
                label = "5"
            }
            Pre_Design/Link (opt_add_lhs_2, opt_add_lhs_3) {
                label = "6"
            }
        }
        RHS {
            Post_Design/ConstantBlock opt_add_rhs_0 {
                label = "0"
            }
            Post_Design/ConstantBlock opt_add_rhs_1 {
                label = "1"
            }
            Post_Design/AdditionBlock opt_add_rhs_2 {
                label = "2"
            }
            Post_Design/Block opt_add_rhs_3 {
                label = "3"
            }
            Post_Design/Link (opt_add_rhs_0, opt_add_rhs_2) {
                label = "4"
            }
            Post_Design/Link (opt_add_rhs_1, opt_add_rhs_2) {
                label = "5"
            }
            Post_Design/ConstantBlock opt_add_rhs_7 {
                label = "7"
                value_value = $
                            Float function value(model : Element, name : String, mapping : Element):
                                return float_addition(read_attribute(model, mapping["0"], "value"),  read_attribute(model, mapping["1"], "value"))!
                        $
            }
            Post_Design/Link (opt_add_rhs_7, opt_add_rhs_3) {
                label = "8"
            }
        }
    }

    {Contains} ForAll optimize_multiplier {
        LHS {
            Pre_Design/ConstantBlock opt_mult_lhs_0 {
                label = "0"
            }
            Pre_Design/ConstantBlock opt_mult_lhs_1 {
                label = "1"
            }
            Pre_Design/MultiplyBlock opt_mult_lhs_2 {
                label = "2"
            }
            Pre_Design/Block opt_mult_lhs_3 {
                label = "3"
            }
            Pre_Design/Link (opt_mult_lhs_0, opt_mult_lhs_2) {
                label = "4"
            }
            Pre_Design/Link (opt_mult_lhs_1, opt_mult_lhs_2) {
                label = "5"
            }
            Pre_Design/Link (opt_mult_lhs_2, opt_mult_lhs_3) {
                label = "6"
            }
        }
        RHS {
            Post_Design/ConstantBlock opt_mult_rhs_0 {
                label = "0"
            }
            Post_Design/ConstantBlock opt_mult_rhs_1 {
                label = "1"
            }
            Post_Design/MultiplyBlock opt_mult_rhs_2 {
                label = "2"
            }
            Post_Design/Block opt_mult_rhs_3 {
                label = "3"
            }
            Post_Design/Link (opt_mult_rhs_0, opt_mult_rhs_2) {
                label = "4"
            }
            Post_Design/Link (opt_mult_rhs_1, opt_mult_rhs_2) {
                label = "5"
            }
            Post_Design/ConstantBlock opt_mult_rhs_7 {
                label = "7"
                value_value = $
                            Float function value(model : Element, name : String, mapping : Element):
                                return float_multiplication(read_attribute(model, mapping["0"], "value"), read_attribute(model, mapping["1"], "value"))!
                        $
            }
            Post_Design/Link (opt_mult_rhs_7, opt_mult_rhs_3) {
                label = "8"
            }
        }
    }

    {Contains} ForAll optimize_constant {
        LHS {
            Pre_Design/ConstantBlock opt_const_lhs_0 {
                label = "0"
            }
            Pre_Design/Block opt_const_lhs_1 {
                label = "1"
            }
            Pre_Design/ConstantBlock opt_const_lhs_2 {
                label = "2"
            }
            Pre_Design/Block opt_const_lhs_3 {
                label = "3"
            }
            Pre_Design/Link (opt_const_lhs_0, opt_const_lhs_1) {
                label = "4"
            }
            Pre_Design/Link (opt_const_lhs_2, opt_const_lhs_3) {
                label = "5"
            }

            constraint = $
                            Boolean function constraint(model : Element, mapping : Element):
                                return (cast_integer(cast_id(mapping["0"])) < cast_integer(cast_id(mapping["2"])))!
                         $
        }
        RHS {
            Post_Design/ConstantBlock opt_const_rhs_0 {
                label = "0"
            }
            Post_Design/Block opt_const_rhs_1 {
                label = "1"
            }
            Post_Design/ConstantBlock opt_const_rhs_2 {
                label = "2"
            }
            Post_Design/Block opt_const_rhs_3 {
                label = "3"
            }
            Post_Design/Link (opt_const_rhs_0, opt_const_rhs_1) {
                label = "4"
            }
            Post_Design/Link (opt_const_rhs_0, opt_const_rhs_3) {
                label = "6"
            }
        }
    }

    {Contains} Atomic map_to_partial {
        LHS {}
        RHS {
            action = $
                        Void function action(model : Element, mapping : Element):
                            Element all_blocks
                            String element_name
                            String new_element_name
                            String mm_type_name
                            Element all_links

                            all_blocks = allInstances(model, "Design/Block")
                            while (set_len(all_blocks) > 0):
                                element_name = set_pop(all_blocks)
                                mm_type_name = "PartialRuntime/" + cast_string(list_read(string_split(read_type(model, element_name), "/"), 1))

                                if (set_len(allOutgoingAssociationInstances(model, element_name, "D2P_block")) == 0):
                                    // New design element, so create in partial runtime model as well
                                    String new_name
                                    if (mm_type_name == "PartialRuntime/DelayBlock"):
                                        new_name = "PartialRuntime/" + string_replace(element_name, "/", "_")
                                    else:
                                        new_name = ""
                                    new_element_name = instantiate_node(model, mm_type_name, new_name)
                                    instantiate_link(model, "D2P_block", "", element_name, new_element_name)

                                // Always update the value of attributes of PartialRuntime
                                new_element_name = map_D2P(model, element_name)
                                if (mm_type_name == "PartialRuntime/ConstantBlock"):
                                    instantiate_attribute(model, new_element_name, "value", read_attribute(model, element_name, "value"))
                                elif (mm_type_name == "PartialRuntime/ProbeBlock"):
                                    instantiate_attribute(model, new_element_name, "name", read_attribute(model, element_name, "name"))

                            all_blocks = allInstances(model, "PartialRuntime/Block")
                            while (set_len(all_blocks) > 0):
                                element_name = set_pop(all_blocks)
                                if (set_len(allIncomingAssociationInstances(model, element_name, "D2P_block")) == 0):
                                    // Old partial runtime element, so remove
                                    model_delete_element(model, element_name)

                            // Delete all existing links
                            all_links = allInstances(model, "PartialRuntime/Link")
                            while (set_len(all_links) > 0):
                                model_delete_element(model, set_pop(all_links))

                            all_links = allInstances(model, "PartialRuntime/InitialCondition")
                            while (set_len(all_links) > 0):
                                model_delete_element(model, set_pop(all_links))

                            // Recreate all of them
                            all_links = allInstances(model, "Design/Link")
                            while (set_len(all_links) > 0):
                                element_name = set_pop(all_links)
                                instantiate_link(model, "PartialRuntime/Link", "", map_D2P(model, readAssociationSource(model, element_name)), map_D2P(model, readAssociationDestination(model, element_name)))

                            all_links = allInstances(model, "Design/InitialCondition")
                            while (set_len(all_links) > 0):
                                element_name = set_pop(all_links)
                                instantiate_link(model, "PartialRuntime/InitialCondition", "", map_D2P(model, readAssociationSource(model, element_name)), map_D2P(model, readAssociationDestination(model, element_name)))

                            return!

                        String function map_D2P(model : Element, name : String):
                            Element destinations
                            String pick

                            destinations = allAssociationDestinations(model, name, "D2P_block")

                            pick = name
                            while (pick == name):
                                pick = set_pop(destinations)

                            return pick!
                     $
        }
    }
}

Initial (schedule, expand) {}

OnSuccess (expand, expand) {}
OnFailure (expand, remove_blocks) {}
OnSuccess (remove_blocks, map_to_partial) {}
OnSuccess (map_to_partial, success) {}
