# Running the Program

In this article we continue our quick start series. This is the last article in this series.  We assume that you are following from the previous article.

#### 9. Full Example Code

Below is the complete code with all steps combined:

```cpp
#include <iostream>
#include <chrono>
#include "cofhe.hpp"
#include "node/network_details.hpp"
#include "node/client_node.hpp"
#include "node/compute_request_handler.hpp"

using namespace CoFHE;

int main(int argc, char* argv[]) {
    if (argc < 5) {
        std::cerr << "Usage: " << argv[0] << " <client_ip> <client_port> <setup_ip> <setup_port>" << std::endl;
        return 1;
    }

    auto self_details = NodeDetails{argv[1], argv[2], NodeType::CLIENT_NODE};
    auto setup_node_details = NodeDetails{argv[3], argv[4], NodeType::SETUP_NODE};
    auto client_node = make_client_node<CPUCryptoSystem>(setup_node_details);
    auto& cs = client_node.crypto_system();

    // Step 3: Create tensors of encrypted data
    size_t n = 8, m = 8, p = 8;
    Tensor<CPUCryptoSystem::PlainText*> pt1(n, m, nullptr);
    pt1.flatten();
    for (size_t i = 0; i < n * m; i++) {
        pt1.at(i) = new CPUCryptoSystem::PlainText(cs.make_plaintext(i + 1));
    }

    Tensor<CPUCryptoSystem::PlainText*> pt2(m, p, nullptr);
    pt2.flatten();
    for (size_t i = 0; i < m * p; i++) {
        pt2.at(i) = new CPUCryptoSystem::PlainText(cs.make_plaintext(i + 1));
    }

    pt1.reshape({n, m});
    pt2.reshape({m, p});
    auto ct1 = cs.encrypt_tensor(client_node.network_public_key(), pt1);
    auto ct2 = cs.encrypt_tensor(client_node.network_public_key(), pt2);

    // Step 4: Serialize tensors
    std::string serialized_ct1 = cs.serialize_ciphertext_tensor(ct1);
    std::string serialized_ct2 = cs.serialize_ciphertext_tensor(ct2);

    // Step 5: Create compute request for multiplication
    ComputeRequest::ComputeOperationOperand operand1(
        ComputeRequest::DataType::TENSOR,
        ComputeRequest::DataEncrytionType::CIPHERTEXT,
        serialized_ct1
    );

    ComputeRequest::ComputeOperationOperand operand2(
        ComputeRequest::DataType::TENSOR,
        ComputeRequest::DataEncrytionType::CIPHERTEXT,
        serialized_ct2
    );

    ComputeRequest::ComputeOperationInstance operation(
        ComputeRequest::ComputeOperationType::BINARY,
        ComputeRequest::ComputeOperation::MULTIPLY,
        { operand1, operand2 }
    );

    ComputeRequest req(operation);

    // Step 6: Perform computation
    auto start = std::chrono::high_resolution_clock::now();
    ComputeResponse* res = nullptr;
    client_node.compute(req, &res);
    auto stop = std::chrono::high_resolution_clock::now();

    if (res && res->status() == ComputeResponse::Status::OK) {
        std::cout << "Encrypted result tensor received." << std::endl;
    } else {
        std::cerr << "Computation failed." << std::endl;
        return 1;
    }

    // Step 7: Create compute request for decryption
    ComputeRequest::ComputeOperationOperand decrypt_operand(
        ComputeRequest::DataType::TENSOR,
        ComputeRequest::DataEncrytionType::CIPHERTEXT,
        res->data()
    );

    ComputeRequest::ComputeOperationInstance decrypt_operation(
        ComputeRequest::ComputeOperationType::UNARY,
        ComputeRequest::ComputeOperation::DECRYPT,
        { decrypt_operand }
    );

    ComputeRequest req_d(decrypt_operation);

    client_node.compute(req_d, &res);
    auto stop_d = std::chrono::high_resolution_clock::now();

    if (res && res->status() == ComputeResponse::Status::OK) {
        std::cout << "Decrypted result tensor received." << std::endl;
    } else {
        std::cerr << "Decryption failed." << std::endl;
        return 1;
    }

    // Step 8: Deserialize and display the result
    Tensor<CPUCryptoSystem::PlainText*> result_tensor = cs.deserialize_plaintext_tensor(res->data());
    result_tensor.flatten();

    std::cout << "Resulting matrix after multiplication and decryption:" << std::endl;
    for (size_t idx = 0; idx < result_tensor.size(); ++idx) {
        float value = cs.get_float_from_plaintext(*result_tensor[idx]);
        std::cout << value << " ";
        if ((idx + 1) % p == 0) {
            std::cout << std::endl;
        }
    }

    // Display computation times
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
    auto duration_d = std::chrono::duration_cast<std::chrono::microseconds>(stop_d - stop);

    std::cout << "Time taken by multiplication: " << duration.count() << " microseconds" << std::endl;
    std::cout << "Time taken by decryption: " << duration_d.count() << " microseconds" << std::endl;

    pt1.flatten();
    for (size_t i = 0; i < n * m; i++) {
        delete pt1.at(i);
    }
    pt2.flatten();
    for (size_t i = 0; i < m * p; i++) {
        delete pt2.at(i);
    }
    ct1.flatten();
    for (size_t i = 0; i < n * m; i++) {
        delete ct1.at(i);
    }
    ct2.flatten();
    for (size_t i = 0; i < m * p; i++) {
        delete ct2.at(i);
    }
    result_tensor.flatten();
    for (size_t i = 0; i < n * p; i++) {
        delete result_tensor.at(i);
    }

    return 0;
}
```

#### 10. Compile and Run the Example

**Compilation:**

Make sure to link against the CoFHE library and include paths correctly.

```bash
g++ -std=c++17 -o cofhe_example cofhe_example.cpp -I/path_to_cofhe_lib
```

**Execution:**

Provide the required command-line arguments:

```bash
./cofhe_example <client_ip> <client_port> <setup_ip> <setup_port>
```

Replace `<client_ip>`, `<client_port>`, `<setup_ip>`, and `<setup_port>` with appropriate values.

**Example:**

```bash
./cofhe_example 127.0.0.1 4460 127.0.0.1 4455
```

### Conclusion

This basic tutorials series demonstrates how to use the CoFHE library to perform secure matrix multiplication over encrypted data using tensors. By following the steps outlined, you can adapt this example to perform other homomorphic computations supported by the CoFHE network.

### Additional Tips

* **Memory Management:** Ensure that dynamically allocated memory (like pointers to `CipherText` and `PlainText`) is properly deallocated to prevent memory leaks.
* **Error Handling:** Always check the status of `ComputeResponse` objects to handle errors gracefully.
* **Security Considerations:** Keep your secret keys secure and be cautious when handling plaintext data.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://openvector.gitbook.io/docs/quick-start/editor-5.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
