diff --git a/.gitignore b/.gitignore
index a8c01f5..329da6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,4 +39,6 @@ build/
 
 #CSharp
 bin/
-obj/
\ No newline at end of file
+obj/
+csharp/Tensor.NET/bin/
+csharp/Tensor.NET/obj/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..093454f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "googletest"]
+	path = third_party/googletest
+	url = https://github.com/google/googletest.git
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2a96ad9..53028dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,15 +1,51 @@
 cmake_minimum_required(VERSION 3.11)
 
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_BUILD_TYPE "Debug")
-# set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps")
-# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
+set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-attributes -Wno-unused -Wno-parentheses")
+set(CMAKE_CXX_FLAGS "-fPIC")
+set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+project(TensorNet VERSION 0.1.0)
+
+option(NN_WITH_TEST "Enable test for Num.NET." OFF)
+option(NN_CROSS_COMPILE "Cross compile to build. So far only compile from Linux targeting Windows is supported" OFF)
+
+if(NOT CMAKE_BUILD_TYPE)
+    message(STATUS "Set the cmake build type to RelWithDebInfo since no build type was specified.")
+    set(CMAKE_BUILD_TYPE RelWithDebInfo)
+endif()
+
+if(NOT WIN32)
+    if(NN_CROSS_COMPILE)
+        message(STATUS "Use cross compile mode on linux targeting Windows.")
+        set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Windows/${CMAKE_BUILD_TYPE})
+        set(CMAKE_SYSTEM_NAME Windows)
+
+        set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
+        set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
+        set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+        set(CMAKE_RANLIB x86_64-w64-mingw32-ranlib)
+    else()
+        message(STATUS "Use host compile mode on Linux.")
+        set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SYSTEM_NAME}/${CMAKE_BUILD_TYPE})
+    endif()
+else()
+    message(STATUS "Use host compile mode on Windows.")
+    set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_SYSTEM_NAME}/${CMAKE_BUILD_TYPE})
+endif()
+
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
+message(STATUS "The outputs will be wrote into " ${PROJECT_BINARY_DIR} ".")
+set(CMAKE_OUTPUT)
 
-project(NumNet VERSION 0.1.0)
 
-# link_libraries(NumNet PRIVATE core_base)
 add_subdirectory(./core/base)
-add_subdirectory(./core/test)
 add_subdirectory(./core/op)
 add_subdirectory(./apis)
-add_subdirectory(./third_party/googletest)
\ No newline at end of file
+
+if(NN_WITH_TEST)
+    add_subdirectory(./third_party/googletest)
+    add_subdirectory(./core/test)
+endif()
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
index 22f9c58..7bd38fd 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,40 +1,133 @@
-## Num.NET
-
-### TODO
-
-| Event | Description | Priority | Status |
-| ----- | --------------| --- | ----- |
-| Complete the smallest framework of c++ part | Construct a smallest framework of c++ part which could run naive implemention of Matmul successfully. In this stage, extensibility should be taken into consideration, but details could be ignored. | p0 | Complete ✅ |
-| Complete naive implemention of Matmul | The results of naive implemention of matmul are wrong. Besides, more test cases need to be added, including more shapes, more dtypes and more data. | p0 | Complete ✅ |
-| Add Benchmark test for Matmul | Add some bechmark tests for Matmul to evaluate its effeciency | p1 | Waiting 🔵 |
-| Add deduce to decide the output shape of ops | Checking if the shapes are matched in the body of op is not a good choice. A calculator for it is needed | p0 | Complete ✅ |
-| Add broadcast | Add broadcast with stride and wrap it | p0 | Complete ✅ |
-| Add script to auto build and test | Write a script on linux to build and run all tests automatically | p2 | Waiting 🔵 |
-| Add ```reshape``` | Add ```reshape``` for ```NDArray``` | p0 | Complete ✅ |
-| Add naive op ```dot``` | Add naive implementation for op ```dot``` | p0 | Waiting 🔵 |
-| Add naive op ```transpose``` | Add naive implementation for op ```transpose``` | p0 | Complete ✅ |
-| Add naive op ```permute``` | Add naive implementation for op ```permute``` | p0 | Complete ✅ |
-| Add naive op ```add``` | Add naive implementation for op ```add``` | p0 | Waiting 🔵 |
-| Add naive op ```sub``` | Add naive implementation for op ```sub``` | p0 | Waiting 🔵 |
-| Add naive op ```mul``` | Add naive implementation for op ```mul``` | p0 | Waiting 🔵 |
-| Add naive op ```div``` | Add naive implementation for op ```div``` | p0 | Waiting 🔵 |
-| Add ```type_deduce``` | Add deduce method for type of layout to decide the ```dtype``` of the output Array. | p0 | Complete ✅ |
-| Add ```Status``` | Add a status struct to tell the caller if the call success and return error message if failed. | p0 | Complete ✅ |
-| Define actions on Debug and Release mode | define different actions for one expr on different mode. For instance, ```nn_assert``` should not take action on release mode | p0 | Waiting 🔵 |
-| Add ```Checker``` for test | Add a checker class to judge if the pred and result are matched. | p0 | On going 🚀 |
-| Add Process for overflow | Add process to deal with overflow, show user NAN instead of completely wrong data. | p0 | Waiting 🔵 |
-| Automatically squeeze the shape of scalar to one-dim | Mainly for result of matmul. | p2 | Waiting 🔵 |
-| Add ```slice``` | Add support of ```slice``` | p0 | Waiting 🔵 |
-| Add CSharp interop | Test CSharp interop, with a few basic apis | p0 | Complete ✅ |
-| Add Serialization for .npy file | Provide interface to serialize and deserialize with .npy files | p0 | Waiting 🔵 |
-| Support reading of excel | Provide interface to read and write with .csv and excel file | p1 | Waiting 🔵 |
-| Support type convert | Support type convert of ```Tensor``` | p0 | On going 🚀 |
-| Type deduce for double-input ops | Add type deduce of double-input ops in c++ part | p2 | Complete ✅ |
-| Add self-manipulated type ops | Except for single input and double input ops, there are some ops directly manipulating itself's data, such as IDentity, Ones and so on. | p0 | Waiting 🔵 |
-| Type deduce in C# part | Add type deduce of C# part | p0 | Complete ✅ |
-| Design exceptions in C# part | Design exceptions in C# part | p0 | Complete ✅ |
-| Add unit tests for C# part | Add unit tests for C# part | p0 | Complete ✅ |
-| Add Logger for C# part | Add Logger for C# part, in which new feature of C# 10 could be taken into account | p0 | Waiting 🔵 |
-
-
-✅   ❌   🚀   🔵
+English | [中文](./doc/Readme-zh-cn.md)
+
+# Tensor.NET
+
+Tensor.NET is a lightweight and high-performance tensor library which provides numpy-like operations but .NET style interfaces. It supports generic tensor, Linq, C# native slices and so on.
+
+## Why using Tensor.NET
+
+Currently, there has been some repositories to support tensor operation, providing numpy-like interfaces, such as [NumSharp](https://github.com/SciSharp/NumSharp) and [TensorFlow.NET](https://github.com/SciSharp/TensorFlow.NET). I also learned a lot from them and thanks for the authors of the two repositories sincerely!
+
+However, everyone has his strong and weak points, compared with the previous works, ```Tensor.NET``` provides the features below.
+
+- Faster speed: using c++ to implement operations.
+
+    ```Tensor.NET``` puts its implmentation of operations in c++ and interop with native api. Then a encapsulation is applied to hide the interop from users. It could provide better performance than pure C# implementation. (3 - 30 times faster)
+
+- Extensibility: using generic class ```Tensor<T>```.
+
+    Erasing type in the class design could provide convinience for python-style interfaces. However, using generic class could better integrate with other .NET code.
+
+- Friendly apis for .NETer
+    
+    Rather than providing python-style and numpy-like apis, ```Tensor.NET``` aims to design .NET style apis. It implements ```IEnumerable<T>``` interface, supports C# index and slice, and use generic to constriant the operations between tensors of different data types. ```Tensor.NET``` provides the main features in ```Numpy```, but provides .NET style interfaces.
+
+The following features will be provided in the future.
+
+- Optimize the algorithm of operations on x86.
+
+- Support multi-thread to accelerate the operations.
+
+- Support reading and writing tensor from image, excel, csv and other more formats.
+
+## API map from Numpy to Tensor.NET (WIP)
+
+```Tensor.NET``` implements the main features of ```Numpy```. The mapping from Numpy API to Tensor.NET API could be found [here](./doc/API_Mapping.md).
+
+```Tensor.NET``` is a lightweight library so that some features may not been covered so far. Please submit an issue or PR to tell us the feature you need.
+
+## Installation
+
+```
+PM> Install-Package Tensor.NET
+```
+
+or 
+
+```
+$ dotnet add package Tensor.NET
+```
+
+
+## Documention
+
+- About how to build from source, please refer to [this doc](./doc/Build.md)
+
+- About examples, please refer to [this repository](https://github.com/AsakusaRinne/Tensor.NET.Examples)
+
+## Benchmarks (WIP)
+
+Benchmarks will be privided later.
+
+## Simple usages
+
+```cs
+using Tensornet;
+using Tensornet.Common;
+using Tensornet.Math;
+using System.Text;
+
+// Matrix multiply
+// Create Tensor from arrays.
+Tensor<int> a = Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[] { 1, 3, 2 });
+Tensor<double> b = Tensor.FromArray<double>(new double[]{1.2, 2.6, 3.9, 4.1, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6, 1.2, 2.6, 
+                    3.9, 4.1, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6}, new int[] { 4, 2, 4 });
+var c = a.Matmul(b);
+// Print the tensor
+Console.WriteLine(c);
+
+// Reshape, permute and transpose
+c = c.Reshape(new int[] { 6, 8 }).Permute(new int[] { 1, 0 }).Transpose(0, 1);
+
+// Indexer
+c[0, 0] = 999.0;
+c[5, 7] = -888.0;
+
+// Slice
+var s = c[1..3, ^5..^2];
+
+
+// Random generations
+var gaussian = Tensor.Random.Normal<int>(new TensorShape(5, 6), 0, 5);
+var avg = Tensor.Random.Uniform<int>(new TensorShape(5, 6), -5, 5);
+
+// Other creation methods for Tensor
+var eye = Tensor.Eye<int>(4, 5, 2);
+eye.Fill(258);
+var linspace = Tensor.Linspace<double>(0, 10, 5);
+var ones = Tensor.Ones<int>(new int[] { 2, 3 });
+
+// Squeeze and unsqueeze
+ones = ones.Unsqueeze(1);
+ones = ones.Unsqueeze(3);
+ones = ones.Squeeze();
+
+// Using Linq APIs
+foreach(var item in eye){
+    Console.WriteLine($"{item} ");
+}
+
+// Bool index
+var bidx = Tensor.Random.Uniform<bool>(new int[] { 3, 3 }, 0, 1);
+var bres = ta[ta > 20];
+var twhere = ta.Where(x => x > 20, 999, -555);
+var trefer = Tensor.Random.Uniform<int>(new int[] { 2, 3, 3 }, -100, -50);
+ta[ta > 20] = trefer;
+
+// Serialization and Deserialization
+TensorWriter.Write("test.tn", ta);
+var tr = TensorReader.Read<float>("test.tn");
+
+// argmax and argmin
+var src = Tensor.Random.Uniform<int>(new int[] { 4, 5 }, 0, 50);
+var amax = src.Argmax(0);
+var amin = src.Argmin(1);
+```
+
+## The major shortcoming
+
+Despite the advantages above, ```Tensor.NET``` does have some shortcomings, the main shortcoming is its basic arithment with operator ```+ - % /```.
+
+Since ```Tensor.NET``` use generic class in its design and operations between tensors of different types are supposed, there's no way to provide these operators in .NET 6 and C# 10 except the operations between tensors of the same data type. For tensors with different data types, ```Add, Sub, Mod, Div``` methods are provided. However, still, there will be some inconvinience.
+
+This problem is supposed to be solved in .NET 7 with its new features.
diff --git a/apis/CMakeLists.txt b/apis/CMakeLists.txt
index d8e16f4..d0d95da 100644
--- a/apis/CMakeLists.txt
+++ b/apis/CMakeLists.txt
@@ -1,7 +1,20 @@
 file(GLOB_RECURSE SOURCES ./*.cpp)
 
-add_library(numnet SHARED ${SOURCES})
+message(${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
 
-target_link_libraries(numnet PRIVATE core_op)
-
-target_include_directories(numnet PRIVATE ${PROJECT_SOURCE_DIR})
\ No newline at end of file
+if(NOT WIN32 AND NN_CROSS_COMPILE)
+    add_library(tensornet.dll SHARED ${SOURCES})
+    target_link_libraries(tensornet.dll PRIVATE tensornet_core_op.lib tensornet_core_base.lib)
+    set_target_properties(tensornet.dll PROPERTIES
+        SUFFIX ""
+        LINK_FLAGS "-Wl,--add-stdcall-alias"
+    )
+    target_include_directories(tensornet.dll PRIVATE ${PROJECT_SOURCE_DIR})
+else()
+    add_library(tensornet SHARED ${SOURCES})
+    set_target_properties(tensornet PROPERTIES
+        PREFFIX ""
+    )
+    target_link_libraries(tensornet PRIVATE tensornet_core_op tensornet_core_base)
+    target_include_directories(tensornet PRIVATE ${PROJECT_SOURCE_DIR})
+endif()
\ No newline at end of file
diff --git a/apis/numnet_c_cxx_apis.cpp b/apis/numnet_c_cxx_apis.cpp
index 4d9e3a2..4242f1f 100644
--- a/apis/numnet_c_cxx_apis.cpp
+++ b/apis/numnet_c_cxx_apis.cpp
@@ -7,7 +7,7 @@ using opr::OpBase;
 
 /***************Common Implementations****************/
 
-opr::OpBase* GetImpl(ProviderEnum provider) {
+opr::OpBase *GetImpl(ProviderEnum provider) {
   if (provider == ProviderEnum::Naive) {
     return opr::naive::OpNaiveImpl::get_instance();
   } else {
@@ -16,30 +16,26 @@ opr::OpBase* GetImpl(ProviderEnum provider) {
 }
 
 template <typename ctype>
-void print_data(const Tensor& src) {
+void print_data(const Tensor &src) {
   auto get_real_pos = [&src](int idx) {
     int res = 0;
-    for (int i = src.layout.ndim - 1; i >= 0; i--) {
-      int mod = src.layout.stride[i];
-      if (!mod)
-        mod = src.layout.shape[i] * (i > 0 ? src.layout.stride[i - 1] : 1);
-      else
-        res += idx / mod * mod;
-      idx %= mod;
-      if (!idx) break;
+    int mod = 1;
+    for (int i = src.layout.ndim - 1; i >= 1; i--) mod *= src.layout.shape[i];
+    for (int i = 0; i < src.layout.ndim; i++) {
+      int shape = idx / mod;
+      idx -= shape * mod;
+      res += shape * src.layout.stride[i];
+      if (i < src.layout.ndim - 1) mod /= src.layout.shape[i + 1];
     }
     return res;
   };
 
-  bool a = 1;
-  bool b = 1;
-  bool c = a * b;
   src.layout.dtype.assert_is_ctype<ctype>();
   nn_assert(!src.layout.is_empty(), "Cannot print an empty ndarray.");
   auto ptr = src.ptr<ctype>();
   for (nn_size i = 0; i < src.layout.total_elems(); i++) {
     nn_size mod = 1;
-    for (nn_size j = 0; j < src.layout.ndim; j++) {
+    for (nn_size j = src.layout.ndim - 1; j >= 0; j--) {
       mod *= src.layout.shape[j];
       if (i % mod == 0) {
         std::cout << "[";
@@ -51,12 +47,12 @@ void print_data(const Tensor& src) {
 
     std::cout << ptr[get_real_pos(i)];
 
-    if ((i + 1) % src.layout.shape[0] != 0) std::cout << ",";
+    if ((i + 1) % src.layout.shape[src.layout.ndim - 1] != 0) std::cout << ",";
 
     std::cout << " ";
     mod = 1;
     nn_size hit_times = 0;
-    for (nn_size j = 0; j < src.layout.ndim; j++) {
+    for (nn_size j = src.layout.ndim - 1; j >= 0; j--) {
       mod *= src.layout.shape[j];
       if ((i + 1) % mod == 0) {
         std::cout << "]";
@@ -75,29 +71,26 @@ void print_data(const Tensor& src) {
   std::cout << std::endl;
 }
 
-int GetErrorCode(Status* status) {
-  std::cout << "Jump in code to get error code." << std::endl;
-  return status->code();
-}
+int GetErrorCode(Status *status) { return status->code(); }
 
-const char* GetErrorMessage(Status* status) {
+const char *GetErrorMessage(Status *status) {
   return status->error_message().c_str();
 }
 
-void FreeStatusMemory(Status* status) {
+void FreeStatusMemory(Status *status) {
   free(status);
   status = nullptr;
 }
 
 /*************Operator Implementations*************/
 
-Status* Matmul(NativeTensor* a, NativeTensor* b, NativeTensor* oup,
-               param::matmul* param, ProviderEnum provider) {
+Status *Matmul(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+               param::matmul *param, ProviderEnum provider) {
   Tensor t_a, t_b, t_oup;
   a->ToTensor(t_a, false);
   b->ToTensor(t_b, false);
   oup->ToTensor(t_oup, true);
-  OpBase* impl = GetImpl(provider);
+  OpBase *impl = GetImpl(provider);
   if (impl == nullptr) {
     return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
                       "Unsupported provider.");
@@ -110,12 +103,72 @@ Status* Matmul(NativeTensor* a, NativeTensor* b, NativeTensor* oup,
   }
 }
 
-Status* Permute(NativeTensor* inp, NativeTensor* oup, param::permute* param,
+Status *Dot(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+            param::dot *param, ProviderEnum provider) {
+  Tensor t_a, t_b, t_oup;
+  a->ToTensor(t_a, false);
+  b->ToTensor(t_b, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->dot(t_a, t_b, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Interelem(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+                  param::interelem *param, ProviderEnum provider) {
+  Tensor t_a, t_b, t_oup;
+  a->ToTensor(t_a, false);
+  b->ToTensor(t_b, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->interelem(t_a, t_b, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *BoolIndex(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+                  param::boolindex *param, ProviderEnum provider) {
+  printf("Enter!\n");
+  Tensor t_a, t_b, t_oup;
+  a->ToTensor(t_a, false);
+  b->ToTensor(t_b, false);
+  oup->ToTensor(t_oup, true);
+  print_data<bool>(t_b);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->boolindex(t_a, t_b, t_oup, *param);
+  print_data<int>(t_oup);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Permute(NativeTensor *inp, NativeTensor *oup, param::permute *param,
                 ProviderEnum provider) {
   Tensor t_inp, t_oup;
   inp->ToTensor(t_inp, false);
   oup->ToTensor(t_oup, true);
-  OpBase* impl = GetImpl(provider);
+  OpBase *impl = GetImpl(provider);
   if (impl == nullptr) {
     return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
                       "Unsupported provider.");
@@ -128,12 +181,12 @@ Status* Permute(NativeTensor* inp, NativeTensor* oup, param::permute* param,
   }
 }
 
-Status* Transpose(NativeTensor* inp, NativeTensor* oup, param::transpose* param,
+Status *Transpose(NativeTensor *inp, NativeTensor *oup, param::transpose *param,
                   ProviderEnum provider) {
   Tensor t_inp, t_oup;
   inp->ToTensor(t_inp, false);
   oup->ToTensor(t_oup, true);
-  OpBase* impl = GetImpl(provider);
+  OpBase *impl = GetImpl(provider);
   if (impl == nullptr) {
     return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
                       "Unsupported provider.");
@@ -144,4 +197,377 @@ Status* Transpose(NativeTensor* inp, NativeTensor* oup, param::transpose* param,
   } else {
     return new Status(status);
   }
+}
+
+Status *Repeat(NativeTensor *inp, NativeTensor *oup, param::repeat *param,
+               ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->repeat(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Flip(NativeTensor *inp, NativeTensor *oup, param::flip *param,
+             ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->flip(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *MatrixInverse(NativeTensor *inp, NativeTensor *oup,
+                      param::matrix_inverse *param, ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->matrix_inverse(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Rotate(NativeTensor *inp, NativeTensor *oup, param::rotate *param,
+               ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->rotate(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Pad(NativeTensor *inp, NativeTensor *oup, param::pad *param,
+            ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->pad(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Sort(NativeTensor *inp, NativeTensor *oup, param::sort *param,
+             ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->sort(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Onehot(NativeTensor *inp, NativeTensor *oup, param::onehot *param,
+               ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->onehot(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Sum(NativeTensor *inp, NativeTensor *oup, param::sum *param,
+            ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->sum(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Mean(NativeTensor *inp, NativeTensor *oup, param::mean *param,
+             ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->mean(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Max(NativeTensor *inp, NativeTensor *oup, param::max *param,
+            ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->max(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Min(NativeTensor *inp, NativeTensor *oup, param::min *param,
+            ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->min(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Negative(NativeTensor *inp, NativeTensor *oup, param::negative *param,
+                 ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->negative(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Argmxx(NativeTensor *inp, NativeTensor *oup, param::argmxx *param,
+               ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->argmxx(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *TypeConvert(NativeTensor *inp, NativeTensor *oup, param::convert *param,
+                    ProviderEnum provider) {
+  Tensor t_inp, t_oup;
+  inp->ToTensor(t_inp, false);
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->convert(t_inp, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Concat(NativeTensor *inp, int size, NativeTensor *oup,
+               param::concat *param, ProviderEnum provider) {
+  std::vector<Tensor> tensors(size);
+  std::vector<const Tensor *> tensor_ptrs(size);
+  for (int i = 0; i < size; i++) {
+    (inp + i)->ToTensor(tensors[i], false);
+    tensor_ptrs[i] = &tensors[i];
+  }
+  Tensor t_oup;
+  oup->ToTensor(t_oup, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->concat(tensor_ptrs, t_oup, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Normal(NativeTensor *nt, param::normal *param, ProviderEnum provider) {
+  Tensor t;
+  nt->ToTensor(t, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->normal(t, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Uniform(NativeTensor *nt, param::uniform *param,
+                ProviderEnum provider) {
+  Tensor t;
+  nt->ToTensor(t, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->uniform(t, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Eye(NativeTensor *nt, param::eye *param, ProviderEnum provider) {
+  Tensor t;
+  nt->ToTensor(t, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->eye(t, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Fill(NativeTensor *nt, param::fill *param, ProviderEnum provider) {
+  Tensor t;
+  nt->ToTensor(t, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->fill(t, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Arange(NativeTensor *nt, param::arange *param, ProviderEnum provider) {
+  Tensor t;
+  nt->ToTensor(t, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->arange(t, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
+}
+
+Status *Linspace(NativeTensor *nt, param::linspace *param,
+                 ProviderEnum provider) {
+  Tensor t;
+  nt->ToTensor(t, true);
+  OpBase *impl = GetImpl(provider);
+  if (impl == nullptr) {
+    return new Status(StatusCategory::NUMNET, StatusCode::INVALID_ARGUMENT,
+                      "Unsupported provider.");
+  }
+  auto status = impl->linspace(t, *param);
+  if (status.is_ok()) {
+    return nullptr;
+  } else {
+    return new Status(status);
+  }
 }
\ No newline at end of file
diff --git a/apis/numnet_c_cxx_apis.h b/apis/numnet_c_cxx_apis.h
index 4d0fe18..201775e 100644
--- a/apis/numnet_c_cxx_apis.h
+++ b/apis/numnet_c_cxx_apis.h
@@ -17,11 +17,11 @@ struct NativeTensor {
   DTypeEnum dtype;
   int ndim;
   nn_size offset;
-  nn_size* shape;
-  nn_size* stride;
-  void* data;
+  nn_size *shape;
+  nn_size *stride;
+  void *data;
 
-  void ToTensor(Tensor& t, bool is_mutable = true) {
+  void ToTensor(Tensor &t, bool is_mutable) {
     t.reset_ptr(data, offset, is_mutable, false);
     t.layout.ndim = ndim;
     memcpy(t.layout.shape, shape, sizeof(nn_size) * ndim);
@@ -32,25 +32,92 @@ struct NativeTensor {
 
 /***************************Common APIs**********************************/
 
-opr::OpBase* GetImpl(ProviderEnum provider);
+opr::OpBase *GetImpl(ProviderEnum provider);
 
-int GetErrorCode(Status* status);
+int GetErrorCode(Status *status);
 
-const char* GetErrorMessage(Status* status);
+const char *GetErrorMessage(Status *status);
 
-void FreeStatusMemory(Status* status);
+void FreeStatusMemory(Status *status);
 
 /***************************Operator APIs**********************************/
 
-Status* Matmul(NativeTensor* a, NativeTensor* b, NativeTensor* oup,
-               param::matmul* param, ProviderEnum provider);
+Status *Matmul(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+               param::matmul *param, ProviderEnum provider);
 
-Status* Permute(NativeTensor* inp, NativeTensor* oup, param::permute* param,
+Status *Dot(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+            param::dot *param, ProviderEnum provider);
+
+Status *BoolIndex(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+                  param::boolindex *param, ProviderEnum provider);
+
+Status *Interelem(NativeTensor *a, NativeTensor *b, NativeTensor *oup,
+                  param::interelem *param, ProviderEnum provider);
+
+Status *Permute(NativeTensor *inp, NativeTensor *oup, param::permute *param,
                 ProviderEnum provider);
 
-Status* Transpose(NativeTensor* inp, NativeTensor* oup, param::transpose* param,
+Status *Transpose(NativeTensor *inp, NativeTensor *oup, param::transpose *param,
                   ProviderEnum provider);
 
+Status *Repeat(NativeTensor *inp, NativeTensor *oup, param::repeat *param,
+               ProviderEnum provider);
+
+Status *Flip(NativeTensor *inp, NativeTensor *oup, param::flip *param,
+             ProviderEnum provider);
+
+Status *MatrixInverse(NativeTensor *inp, NativeTensor *oup,
+                      param::matrix_inverse *param, ProviderEnum provider);
+
+Status *Rotate(NativeTensor *inp, NativeTensor *oup, param::rotate *param,
+               ProviderEnum provider);
+
+Status *Pad(NativeTensor *inp, NativeTensor *oup, param::pad *param,
+            ProviderEnum provider);
+
+Status *Sort(NativeTensor *inp, NativeTensor *oup, param::sort *param,
+             ProviderEnum provider);
+
+Status *Onehot(NativeTensor *inp, NativeTensor *oup, param::onehot *param,
+               ProviderEnum provider);
+
+Status *Sum(NativeTensor *inp, NativeTensor *oup, param::sum *param,
+            ProviderEnum provider);
+
+Status *Mean(NativeTensor *inp, NativeTensor *oup, param::mean *param,
+             ProviderEnum provider);
+
+Status *Max(NativeTensor *inp, NativeTensor *oup, param::max *param,
+            ProviderEnum provider);
+
+Status *Min(NativeTensor *inp, NativeTensor *oup, param::min *param,
+            ProviderEnum provider);
+
+Status *Negative(NativeTensor *inp, NativeTensor *oup, param::negative *param,
+                 ProviderEnum provider);
+
+Status *Argmxx(NativeTensor *inp, NativeTensor *oup, param::argmxx *param,
+               ProviderEnum provider);
+
+Status *TypeConvert(NativeTensor *inp, NativeTensor *oup, param::convert *param,
+                    ProviderEnum provider);
+
+Status *Concat(NativeTensor *inp, int size, NativeTensor *oup,
+               param::concat *param, ProviderEnum provider);
+
+Status *Normal(NativeTensor *nt, param::normal *param, ProviderEnum provider);
+
+Status *Uniform(NativeTensor *nt, param::uniform *param, ProviderEnum provider);
+
+Status *Eye(NativeTensor *nt, param::eye *param, ProviderEnum provider);
+
+Status *Fill(NativeTensor *nt, param::fill *param, ProviderEnum provider);
+
+Status *Arange(NativeTensor *nt, param::arange *param, ProviderEnum provider);
+
+Status *Linspace(NativeTensor *nt, param::linspace *param,
+                 ProviderEnum provider);
+
 #if __cplusplus
 }
 #endif
\ No newline at end of file
diff --git a/core/base/CMakeLists.txt b/core/base/CMakeLists.txt
index 846bbc4..4f8a46e 100644
--- a/core/base/CMakeLists.txt
+++ b/core/base/CMakeLists.txt
@@ -1,10 +1,15 @@
-
-# set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps")
-
 set(CORE_BASE_SRC ./impl)
 file(GLOB_RECURSE SOURCES ${CORE_BASE_SRC}/*.cpp)
 
-# message(${PROJECT_SOURCE_DIR})
+if(NOT WIN32 AND NN_CROSS_COMPILE)
+    add_library(tensornet_core_base.lib STATIC ${SOURCES})
+    set_target_properties(tensornet_core_base.lib PROPERTIES
+        SUFFIX ""
+        LINK_FLAGS "-Wl,--add-stdcall-alias"
+    )
+    target_include_directories(tensornet_core_base.lib PRIVATE ${PROJECT_SOURCE_DIR})
+else()
+    add_library(tensornet_core_base STATIC ${SOURCES})
+    target_include_directories(tensornet_core_base PRIVATE ${PROJECT_SOURCE_DIR})
+endif()
 
-add_library(core_base SHARED ${SOURCES})
-target_include_directories(core_base PUBLIC ${PROJECT_SOURCE_DIR})
diff --git a/core/base/impl/dtype.cpp b/core/base/impl/dtype.cpp
index 20cfcbc..40cdfdf 100644
--- a/core/base/impl/dtype.cpp
+++ b/core/base/impl/dtype.cpp
@@ -26,6 +26,10 @@ DType DType::from_enum(DTypeEnum enumv) {
     return dtype::_dt();
     NN_FOREACH_DTYPE(cb)
 #undef cb
+    default:
+      nn_throw(
+          "Failed to create Dtype from enum because of invalid DTypeEnum "
+          "value.");
   }
   nn_throw(
       "Failed to create Dtype from enum because of invalid DTypeEnum value.");
diff --git a/core/base/impl/layout.cpp b/core/base/impl/layout.cpp
index 6222633..d8c8b6e 100644
--- a/core/base/impl/layout.cpp
+++ b/core/base/impl/layout.cpp
@@ -9,16 +9,11 @@
 namespace nncore {
 Shape::Shape(const std::vector<nn_size> &init_shape) {
   nn_assert(init_shape.size() <= MAX_NDIM,
-            "The shape you specified has too many dims, which is %lu, the "
+            "The shape you specified has too many dims, which is %llu, the "
             "max is %d\n",
             init_shape.size(), MAX_NDIM);
   ndim = init_shape.size();
-  for (nn_size i = 0; i < ndim; i++) {
-    shape[i] = init_shape[ndim - 1 - i];
-  }
-  for (nn_size i = ndim; i < MAX_NDIM; i++) {
-    shape[i] = 1;
-  }
+  memcpy(shape, init_shape.data(), sizeof(nn_size) * ndim);
 }
 
 Shape::Shape(nn_size *init_shape, nn_size ndim) {
@@ -50,7 +45,7 @@ bool Shape::is_shape(const Shape &rhs) const {
 bool Shape::is_equivalent_shape(const Shape &rhs) const {
   nn_size min_ndim = ndim > rhs.ndim ? rhs.ndim : ndim;
   for (nn_size i = 0; i < min_ndim; i++) {
-    if (shape[i] != rhs.shape[i]) return false;
+    if (shape[ndim - i - 1] != rhs.shape[rhs.ndim - i - 1]) return false;
   }
   for (nn_size i = min_ndim; i < ndim; i++) {
     if (shape[i] != 1) return false;
@@ -71,7 +66,7 @@ std::string Shape::to_string() const {
   std::string r = "{";
   if (ndim > 0) {
     for (nn_size i = 0; i < ndim; i++) {
-      r += std::to_string(shape[ndim - 1 - i]);
+      r += std::to_string(shape[i]);
       if (i != ndim - 1) r += ", ";
     }
   }
@@ -91,10 +86,9 @@ Layout::Layout(const Shape &shape, const DType &dtype)
 Layout::Layout(const Shape &shape, const std::vector<nn_size> &stride,
                const DType &dtype)
     : Shape(shape), dtype(dtype) {
-  nn_assert(shape.ndim == stride.size(),
+  nn_assert(shape.ndim == static_cast<nn_size>(stride.size()),
             "Size of shape mismatched that of stride.");
-  for (nn_size i = 0; i < shape.ndim; i++)
-    this->stride[i] = stride[ndim - i - 1];
+  for (nn_size i = 0; i < shape.ndim; i++) this->stride[i] = stride[i];
 }
 
 nn_size Layout::init_contiguous_stride() {
@@ -102,8 +96,8 @@ nn_size Layout::init_contiguous_stride() {
   nn_assert(ndim <= Layout::MAX_NDIM);
   nn_size s = 1;
   for (nn_size i = 0; i < ndim; i++) {
-    stride[i] = s;
-    s *= shape[i];
+    stride[ndim - i - 1] = s;
+    s *= shape[ndim - i - 1];
   }
   return s;
 }
@@ -130,18 +124,19 @@ void Layout::broadcast_inplace(const Shape &target) {
             "dst_shape: src_shape=%s dst_shape=%s",
             to_string().c_str(), target.to_string().c_str());
   for (nn_size i = 0; i < target.ndim; ++i) {
-    nn_size cur_shape = (i < ndim ? shape[i] : 1),
-            cur_stride = (i < ndim ? stride[i] : 0);
-    if (target.shape[i] != cur_shape) {
+    nn_size target_idx = target.ndim - i - 1;
+    nn_size cur_shape = i < ndim ? shape[ndim - i - 1] : 1,
+            cur_stride = i < ndim ? stride[ndim - i - 1] : 0;
+    if (target.shape[target_idx] != cur_shape) {
       nn_assert(cur_shape == 1 || cur_stride == 0,
                 "broadcast on dim with shape not equal to 1: "
                 "src_shape=%s dst_shape=%s",
                 to_string().c_str(), target.to_string().c_str());
-      shape[i] = target.shape[i];
-      stride[i] = 0;
+      shape[target_idx] = target.shape[target_idx];
+      stride[target_idx] = 0;
     } else {
-      shape[i] = cur_shape;
-      stride[i] = cur_stride;
+      shape[target_idx] = cur_shape;
+      stride[target_idx] = cur_stride;
     }
   }
   ndim = target.ndim;
@@ -168,9 +163,9 @@ bool Layout::is_equivalent_layout(const Layout &rhs) const {
 
 void Layout::offset_to_indices(nn_size offset, nn_size *indices) const {
   for (nn_size i = 0; i < ndim; i++) {
-    nn_size idx = ndim - i - 1;
-    indices[idx] = offset / stride[idx];
-    offset %= stride[idx];
+    int mod = stride[i] != 0 ? stride[i] : shape[i];
+    indices[i] = offset / mod;
+    offset %= mod;
   }
 }
 
@@ -189,12 +184,12 @@ std::string Layout::to_string() const {
   } else {
     r += "shape = {";
     for (nn_size i = 0; i < ndim; i++) {
-      r += std::to_string(shape[ndim - 1 - i]);
+      r += std::to_string(shape[i]);
       if (i != ndim - 1) r += ", ";
     }
     r += "}, stride = {";
     for (nn_size i = 0; i < ndim; i++) {
-      r += std::to_string(stride[ndim - 1 - i]);
+      r += std::to_string(stride[i]);
       if (i != ndim - 1) r += ", ";
     }
     r += "}";
@@ -210,10 +205,10 @@ nn_size Layout::content_bytes() const { return total_elems() * dtype.size(); }
 Layout Layout::dimshuffle(const std::vector<nn_size> &dims) const {
   Layout res{dtype};
   res.ndim = this->ndim;
-  nn_assert(dims.size() == this->ndim);
+  nn_assert(static_cast<nn_size>(dims.size()) == this->ndim);
   auto ndim = this->ndim;
   rep(i, ndim) {
-    auto dest = dims[res.ndim - i - 1];
+    auto dest = dims[i];
     nn_assert(dest < ndim);
     res.shape[i] = this->shape[dest];
     res.stride[i] = this->stride[dest];
@@ -229,8 +224,8 @@ Layout Layout::remove_axis(nn_size idx) const {
 
 void Layout::remove_axis_inplace(nn_size axis) {
   nn_assert(ndim >= 2 && axis < ndim);
-  --ndim;
-  for (nn_size i = axis; i < ndim; ++i) {
+  ndim--;
+  for (nn_size i = axis; i < ndim; i++) {
     shape[i] = shape[i + 1];
     stride[i] = stride[i + 1];
   }
@@ -252,20 +247,20 @@ void Layout::add_axis_inplace(nn_size axis, nn_size shape, nn_size stride) {
 bool Layout::is_contiguous() const {
   nn_size expected = 1;
   for (nn_size i = 0; i < ndim; ++i) {
-    if (shape[i] != 1 && stride[i] != expected) return false;
-    expected *= shape[i];
+    if (shape[ndim - i - 1] != 1 && stride[ndim - i - 1] != expected)
+      return false;
+    expected *= shape[ndim - i - 1];
   }
   // empty tensors are not contiguous
   return expected != 0;
 }
 
 Layout Layout::collapse_contiguous() const {
-  // assert_valid(layout);
   nn_assert(ndim);
   Layout res{*this};
 
   // remove all dims with shape 1
-  for (nn_size i = 0; i <= res.ndim - 1 && res.ndim >= 2; ++i) {
+  for (nn_size i = 0; i <= res.ndim - 1 && res.ndim >= 2; i++) {
     if (!res.shape[i]) {
       // empty tensor
       res.ndim = 1;
@@ -278,20 +273,18 @@ Layout Layout::collapse_contiguous() const {
 
   if (res.ndim == 1) {
     if (res.shape[0] <= 1) {
-      // make it the "most canonical" contiguous layout for scalars or
-      // empty tensors
       res.stride[0] = 1;
     }
     return res;
   }
 
   nn_assert(res.ndim && res.shape[res.ndim - 1]);
-  for (nn_size i = 1; i <= res.ndim - 1; ++i) {
+  for (int i = static_cast<int>(res.ndim) - 2; i >= 0; i--) {
     nn_assert(res.shape[i]);
-    if (res.stride[i] == res.stride[i - 1] * res.shape[i - 1]) {
-      res.shape[i] *= res.shape[i - 1];
-      res.stride[i] = res.stride[i - 1];
-      res.remove_axis_inplace(i - 1);
+    if (res.stride[i] == res.stride[i + 1] * res.shape[i + 1]) {
+      res.shape[i] *= res.shape[i + 1];
+      res.stride[i] = res.stride[i + 1];
+      res.remove_axis_inplace(i + 1);
     }
   }
   return res;
@@ -317,17 +310,15 @@ bool Layout::try_reshape(Layout &result, const Shape &tshp,
 
   // So far only the swap of width and height is supported.
   if (is_image) {
-    nn_assert(this->ndim >= 2 && tshp.ndim >= 2 &&
-              this->shape[0] == tshp.shape[1] &&
-              this->shape[1] == tshp.shape[0]);
-    for (nn_size i = 2; i < this->ndim; i++) {
-      if (tshp.ndim > i && tshp.shape[i] != this->shape[i]) {
-        return false;
-      }
+    nn_assert(this->ndim >= 2 && tshp.ndim == this->ndim &&
+              this->shape[ndim - 1] == tshp.shape[tshp.ndim - 2] &&
+              this->shape[ndim - 2] == tshp.shape[tshp.ndim - 1]);
+    for (nn_size i = 0; i < this->ndim - 2; i++) {
+      if (tshp.shape[i] != this->shape[i]) return false;
     }
     result.dtype = this->dtype;
     result.Shape::operator=(tshp);
-    std::swap(result.stride[0], result.stride[1]);
+    std::swap(result.stride[this->ndim - 1], result.stride[this->ndim - 2]);
     return true;
   }
 
@@ -340,7 +331,25 @@ bool Layout::try_reshape(Layout &result, const Shape &tshp,
     return true;
   }
 
-  result.init_contiguous_stride();
+  nn_size sdim = 0, prod = 1, cont_sdim = 0;
+  for (nn_size i = 0; i < tshp.ndim; i++) {
+    nn_assert(cont_sdim < cont.ndim);
+    prod *= result.shape[i];
+    if (prod > cont.shape[cont_sdim]) return false;
+
+    if (prod == cont.shape[cont_sdim] &&
+        (i + 1 >= tshp.ndim || tshp.shape[i + 1] != 1)) {
+      auto s = cont.stride[cont_sdim];
+      for (int j = i; j >= sdim; j--) {
+        result.stride[j] = s;
+        s *= result.shape[j];
+      }
+      cont_sdim++;
+      sdim = i + 1;
+      prod = 1;
+    }
+  }
+  nn_assert(cont_sdim == cont.ndim);
 
   return true;
 }
diff --git a/core/base/include/dtype.h b/core/base/include/dtype.h
index 68baf08..7d7b176 100644
--- a/core/base/include/dtype.h
+++ b/core/base/include/dtype.h
@@ -119,8 +119,8 @@ NN_FOREACH_DTYPE(NN_DECLARE_DTYPE)
   template <>                                       \
   struct DTypeTrait<dtype::_name> {                 \
     NN_DEF_DTYPE_TRAIT_BASIC(_name, _ctype, _bits)  \
-    static ctype min() { return max_val_##_ctype; } \
-    static ctype max() { return min_val_##_ctype; } \
+    static ctype min() { return min_val_##_ctype; } \
+    static ctype max() { return max_val_##_ctype; } \
   };
 
 NN_FOREACH_DTYPE_TRAIT(NN_DEF_DTYPE_TRAIT)
diff --git a/core/base/include/tensor.h b/core/base/include/tensor.h
index 7f85aba..f731b33 100644
--- a/core/base/include/tensor.h
+++ b/core/base/include/tensor.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <iostream>
 #include <memory>
 
 #include "core/base/include/layout.h"
@@ -67,10 +68,10 @@ class RefPtr {
   }
 
   ~RefPtr() {
-    if (m_owned) {
+    if (m_owned && m_ref.use_count() == 1 && *m_ref != nullptr) {
       free(*m_ref);
+      *m_ref = nullptr;
     }
-    m_ref.reset();
   }
 };
 
@@ -115,7 +116,9 @@ struct Tensor {
 
   void reset_ptr(void *ptr, nn_size offset, bool is_mutable, bool is_owner);
 
-  bool is_ptr_owner() { return m_ref_ptr.is_owned(); }
+  bool is_ptr_owner() const { return m_ref_ptr.is_owned(); }
+
+  bool is_mutable() const { return m_ref_ptr.is_mutable(); }
 
  private:
   RefPtr m_ref_ptr;
diff --git a/core/op/CMakeLists.txt b/core/op/CMakeLists.txt
index 9820cea..dfb09f6 100644
--- a/core/op/CMakeLists.txt
+++ b/core/op/CMakeLists.txt
@@ -1,10 +1,15 @@
-
 file(GLOB_RECURSE SOURCES naive/*.cpp common/*.cpp)
 
-# set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps")
-
-# message(${SOURCES})
-
-add_library(core_op SHARED ${SOURCES})
-target_link_libraries(core_op PUBLIC core_base)
-target_include_directories(core_op PUBLIC ${PROJECT_SOURCE_DIR})
+if(NOT WIN32 AND NN_CROSS_COMPILE)
+    add_library(tensornet_core_op.lib STATIC ${SOURCES})
+    target_link_libraries(tensornet_core_op.lib PRIVATE tensornet_core_base.lib)
+    set_target_properties(tensornet_core_op.lib PROPERTIES
+        SUFFIX ""
+        LINK_FLAGS "-Wl,--add-stdcall-alias"
+    )
+    target_include_directories(tensornet_core_op.lib PRIVATE ${PROJECT_SOURCE_DIR})
+else()
+    add_library(tensornet_core_op STATIC ${SOURCES})
+    target_link_libraries(tensornet_core_op PRIVATE tensornet_core_base)
+    target_include_directories(tensornet_core_op PRIVATE ${PROJECT_SOURCE_DIR})
+endif()
diff --git a/core/op/common/argmxx.cpp b/core/op/common/argmxx.cpp
new file mode 100644
index 0000000..8e0ddfd
--- /dev/null
+++ b/core/op/common/argmxx.cpp
@@ -0,0 +1,21 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(argmxx) {
+  if (param.axis < 0 || param.axis >= inp.ndim) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "Invalid argmxx axis param.");
+  }
+  res.dtype = dtype::Int64();
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  res[param.axis] = 1;
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/boolindex.cpp b/core/op/common/boolindex.cpp
new file mode 100644
index 0000000..6436667
--- /dev/null
+++ b/core/op/common/boolindex.cpp
@@ -0,0 +1,28 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(boolindex) {
+  if (a.ndim < b.ndim) {
+    return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+                  "Cannot broadcast bool index to the shape of target tensor.");
+  }
+  for (nn_size i = 0, j = 0; i < a.ndim && j < b.ndim; i++, j++) {
+    nn_size a_idx = a.ndim - i - 1;
+    nn_size b_idx = b.ndim - j - 1;
+    if (a.shape[a_idx] != b.shape[b_idx] && a.shape[a_idx] != 1 &&
+        b.shape[b_idx] != 1) {
+      return Status(
+          StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+          "Cannot broadcast bool index to the shape of target tensor.");
+    }
+  }
+  b.broadcast_inplace(a);
+
+  res = Layout(a);
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/concat.cpp b/core/op/common/concat.cpp
new file mode 100644
index 0000000..6a7dc3c
--- /dev/null
+++ b/core/op/common/concat.cpp
@@ -0,0 +1,50 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+Status OpBase::deduce_layout_concat(const std::vector<const Tensor*>& inp,
+                                    Layout& res, const param::concat& param) {
+  nn_size n = static_cast<nn_size>(inp.size());
+  if (n < 2) {
+    return Status(
+        StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+        "At least two tensor should be inputted to execute the concat op.");
+  }
+  nn_size ndim = inp[0]->layout.ndim;
+  nn_size size_on_axis = inp[0]->layout.shape[param.axis];
+  for (nn_size i = 1; i < n; i++) {
+    if (inp[i]->layout.ndim != ndim) {
+      return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                    "The tensors to concat must have the same ndim.");
+    }
+    if (!inp[i]->layout.dtype.is_same_with(inp[0]->layout.dtype)) {
+      return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                    "Tensors to concat must have the same dtype.");
+    }
+    for (nn_size j = 0; j < ndim; j++) {
+      if (j != param.axis &&
+          inp[i]->layout.shape[j] != inp[0]->layout.shape[j]) {
+        return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                      "Tensors to concat should have the same shape except the "
+                      "specified axis.");
+      }
+    }
+    size_on_axis += inp[i]->layout.shape[param.axis];
+  }
+  if (param.axis < 0 || param.axis >= ndim) {
+    return Status(
+        StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+        "The specified axis to concat exceeds the max ndim of the tensors.");
+  }
+  res.dtype = inp[0]->layout.dtype;
+  res.ndim = ndim;
+  for (nn_size i = 0; i < ndim; i++) {
+    res.shape[i] = inp[0]->layout.shape[i];
+  }
+  res.shape[param.axis] = size_on_axis;
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/convert.cpp b/core/op/common/convert.cpp
new file mode 100644
index 0000000..66c73c2
--- /dev/null
+++ b/core/op/common/convert.cpp
@@ -0,0 +1,21 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+Status OpBase::deduce_layout_convert(Layout& inp, Layout& res,
+                                     const param::convert& param) {
+  if (param.target_type == DTypeEnum::Invalid) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "Invalid type convert param.");
+  }
+  res.dtype = DType::from_enum(param.target_type);
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/dot.cpp b/core/op/common/dot.cpp
index 05bb980..74d1977 100644
--- a/core/op/common/dot.cpp
+++ b/core/op/common/dot.cpp
@@ -4,7 +4,38 @@ namespace nncore {
 namespace opr {
 
 IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(dot) {
-  return Status(StatusCategory::NUMNET, StatusCode::NOT_IMPLEMENTED);
+  auto dim = std::max(a.ndim, b.ndim);
+  for (nn_size i = 0, j = 0, k = 0; i < a.ndim && j < b.ndim; i++, j++, k++) {
+    nn_size a_idx = a.ndim - i - 1;
+    nn_size b_idx = b.ndim - j - 1;
+    if (a.shape[a_idx] != b.shape[b_idx] && a.shape[a_idx] != 1 &&
+        b.shape[b_idx] != 1) {
+      return Status(
+          StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+          "Cannot broadcast bool index to the shape of target tensor.");
+    } else if (a.shape[a_idx] == b.shape[b_idx]) {
+      res.shape[dim - k - 1] = a.shape[a_idx];
+    } else if (a.shape[a_idx] == 1) {
+      res.shape[dim - k - 1] = b.shape[b_idx];
+    } else if (b.shape[a_idx] == 1) {
+      res.shape[dim - k - 1] = a.shape[a_idx];
+    } else {
+      return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+                    "Unknown error when deducing the layout.");
+    }
+  }
+  for (int i = std::min(a.ndim, b.ndim); i < a.ndim; i++) {
+    res.shape[dim - i - 1] = a.shape[a.ndim - i - 1];
+  }
+  for (int i = std::min(a.ndim, b.ndim); i < b.ndim; i++) {
+    res.shape[dim - i - 1] = b.shape[b.ndim - i - 1];
+  }
+  res.dtype = DType::from_enum(
+      deduce_double_input_op(a.dtype.enumv(), b.dtype.enumv()));
+  res.ndim = dim;
+  a.broadcast_inplace(res);
+  b.broadcast_inplace(res);
+  return Status::OK();
 }
 
 }  // namespace opr
diff --git a/core/op/common/dtype_deduce.h b/core/op/common/dtype_deduce.h
index 1cc9b76..0e6ed90 100644
--- a/core/op/common/dtype_deduce.h
+++ b/core/op/common/dtype_deduce.h
@@ -5,6 +5,22 @@
 namespace nncore {
 namespace opr {
 
+inline DTypeEnum deduce_double_input_op(DTypeEnum a, DTypeEnum b) {
+  if (a == DTypeEnum::Float64 || b == DTypeEnum::Float64) {
+    return DTypeEnum::Float64;
+  } else if (a == DTypeEnum::Float32 || b == DTypeEnum::Float32) {
+    return DTypeEnum::Float32;
+  } else if (a == DTypeEnum::Int64 || b == DTypeEnum::Int64) {
+    return DTypeEnum::Int64;
+  } else if (a == DTypeEnum::Int32 || b == DTypeEnum::Int32) {
+    return DTypeEnum::Int32;
+  } else if (a == DTypeEnum::Bool || b == DTypeEnum::Bool) {
+    return DTypeEnum::Bool;
+  } else {
+    nn_throw("Type deduce failed.");
+  }
+}
+
 #define DEDUCE_CASE(_value, _A, _B, _C, _name, ...)              \
   case (_value):                                                 \
     nn_return_status_if_error((_name##_internal<_A, _B, _C>(     \
@@ -76,6 +92,13 @@ namespace opr {
   cb(nn_bool, nn_int64, nn_int64, __VA_ARGS__);        \
   cb(nn_bool, nn_bool, nn_bool, __VA_ARGS__);
 
+#define TYPE_SELECT_SELF_MODIFY(_type, _name)                      \
+  if (t.layout.dtype.is_ctype<_type>()) {                          \
+    nn_return_status_if_error(                                     \
+        _name##_internal<_type>(t.ptr<_type>(), t.layout, param)); \
+    return Status::OK();                                           \
+  }
+
 #define TYPE_SELECT_SINGLE_INPUT(_type, _name, _loup)             \
   if (_loup.dtype.is_ctype<_type>()) {                            \
     nn_return_status_if_error(_name##_internal<_type>(            \
@@ -83,11 +106,35 @@ namespace opr {
     return Status::OK();                                          \
   }
 
+#define TYPE_SELECT_SINGLE_INPUT_SPECIFIED_TYPE(_type, _name, _loup, _typeOup) \
+  if (linp.dtype.is_ctype<_type>()) {                                          \
+    nn_return_status_if_error(_name##_internal<_type>(                         \
+        inp.ptr<_type>(), oup.ptr<_typeOup>(), linp, _loup, param));           \
+    return Status::OK();                                                       \
+  }
+
+#define TYPE_SELECT_CONCAT(_type, _loup)                              \
+  if (_loup.dtype.is_ctype<_type>()) {                                \
+    nn_return_status_if_error(                                        \
+        concat_internal<_type>(inp, oup.ptr<_type>(), _loup, param)); \
+    return Status::OK();                                              \
+  }
+
+#define SPECIFY_SELF_MODIFY_OP_INTERNAL(_type, _class_name, _op_name) \
+  template Status _class_name::_op_name##_internal<_type>(            \
+      _type * t, const Layout& layout, const param::_op_name& param);
+
 #define SPECIFY_SINGLE_OUTPUT_OP_INTERNAL(_type, _class_name, _op_name)     \
   template Status _class_name::_op_name##_internal<_type>(                  \
       const _type* inp, _type* oup, const Layout& linp, const Layout& loup, \
       const param::_op_name& param);
 
+#define SPECIFY_SINGLE_OUTPUT_SPECIFIED_TYPE_OP_INTERNAL(_type, _class_name,   \
+                                                         _op_name, _typeOup)   \
+  template Status _class_name::_op_name##_internal<_type>(                     \
+      const _type* inp, _typeOup* oup, const Layout& linp, const Layout& loup, \
+      const param::_op_name& param);
+
 #define SPECIFY_DOUBLE_OUTPUT_OP_INTERNAL(_typeA, _typeB, _typeC, _class_name, \
                                           _op_name)                            \
   template Status _class_name::_op_name##_internal<_typeA, _typeB, _typeC>(    \
@@ -95,5 +142,54 @@ namespace opr {
       const Layout& la, const Layout& lb, const Layout& loup,                  \
       const param::_op_name& param)
 
+#define SPECIFY_CONVERT_OP_INTERNAL(_typeA, _typeB, _, _class_name) \
+  template Status _class_name::convert_internal<_typeA, _typeB>(    \
+      const _typeA* ptr_inp, _typeB* ptr_oup, const Layout& linp,   \
+      const Layout& loup, [[maybe_unused]] const param::convert& param)
+
+#define CONVERT_CASE(_value, _A, _B, ...)                                      \
+  case (_value):                                                               \
+    nn_return_status_if_error((                                                \
+        convert_internal<_A, _B>(inp.ptr<_A>(), oup.ptr<_B>(), __VA_ARGS__))); \
+    break;
+
+#define TYPE_CONVERT_DEDUCE(_DA, _DB, ...)                                  \
+  {                                                                         \
+    int _value = static_cast<int>(_DA) * 10 + static_cast<int>(_DB);        \
+    switch (_value) {                                                       \
+      CONVERT_CASE(11, nn_int32, nn_int32, __VA_ARGS__)                     \
+      CONVERT_CASE(12, nn_int32, nn_float32, __VA_ARGS__)                   \
+      CONVERT_CASE(13, nn_int32, nn_float64, __VA_ARGS__)                   \
+      CONVERT_CASE(21, nn_float32, nn_int32, __VA_ARGS__)                   \
+      CONVERT_CASE(22, nn_float32, nn_float32, __VA_ARGS__)                 \
+      CONVERT_CASE(23, nn_float32, nn_float64, __VA_ARGS__)                 \
+      CONVERT_CASE(31, nn_float64, nn_int32, __VA_ARGS__)                   \
+      CONVERT_CASE(32, nn_float64, nn_float32, __VA_ARGS__)                 \
+      CONVERT_CASE(33, nn_float64, nn_float64, __VA_ARGS__)                 \
+                                                                            \
+      CONVERT_CASE(14, nn_int32, nn_int64, __VA_ARGS__)                     \
+      CONVERT_CASE(41, nn_int64, nn_int32, __VA_ARGS__)                     \
+      CONVERT_CASE(44, nn_int64, nn_int64, __VA_ARGS__)                     \
+                                                                            \
+      CONVERT_CASE(42, nn_int64, nn_float32, __VA_ARGS__)                   \
+      CONVERT_CASE(43, nn_int64, nn_float64, __VA_ARGS__)                   \
+      CONVERT_CASE(24, nn_float32, nn_int64, __VA_ARGS__)                   \
+      CONVERT_CASE(34, nn_float64, nn_int64, __VA_ARGS__)                   \
+                                                                            \
+      CONVERT_CASE(15, nn_int32, nn_bool, __VA_ARGS__)                      \
+      CONVERT_CASE(51, nn_bool, nn_int32, __VA_ARGS__)                      \
+      CONVERT_CASE(25, nn_float32, nn_bool, __VA_ARGS__)                    \
+      CONVERT_CASE(52, nn_bool, nn_float32, __VA_ARGS__)                    \
+      CONVERT_CASE(35, nn_float64, nn_bool, __VA_ARGS__)                    \
+      CONVERT_CASE(53, nn_bool, nn_float64, __VA_ARGS__)                    \
+      CONVERT_CASE(45, nn_int64, nn_bool, __VA_ARGS__)                      \
+      CONVERT_CASE(54, nn_bool, nn_int64, __VA_ARGS__)                      \
+      CONVERT_CASE(55, nn_bool, nn_bool, __VA_ARGS__)                       \
+      default:                                                              \
+        return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_DTYPE, \
+                      "Type convert failed.");                              \
+    }                                                                       \
+  }
+
 }  // namespace opr
 }  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/flip.cpp b/core/op/common/flip.cpp
new file mode 100644
index 0000000..cd31d0a
--- /dev/null
+++ b/core/op/common/flip.cpp
@@ -0,0 +1,16 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(flip) {
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/interelem.cpp b/core/op/common/interelem.cpp
new file mode 100644
index 0000000..3cc2b4f
--- /dev/null
+++ b/core/op/common/interelem.cpp
@@ -0,0 +1,42 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(interelem) {
+  auto dim = std::max(a.ndim, b.ndim);
+  for (nn_size i = 0, j = 0, k = 0; i < a.ndim && j < b.ndim; i++, j++, k++) {
+    nn_size a_idx = a.ndim - i - 1;
+    nn_size b_idx = b.ndim - j - 1;
+    if (a.shape[a_idx] != b.shape[b_idx] && a.shape[a_idx] != 1 &&
+        b.shape[b_idx] != 1) {
+      return Status(
+          StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+          "Cannot broadcast bool index to the shape of target tensor.");
+    } else if (a.shape[a_idx] == b.shape[b_idx]) {
+      res.shape[dim - k - 1] = a.shape[a_idx];
+    } else if (a.shape[a_idx] == 1) {
+      res.shape[dim - k - 1] = b.shape[b_idx];
+    } else if (b.shape[a_idx] == 1) {
+      res.shape[dim - k - 1] = a.shape[a_idx];
+    } else {
+      return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+                    "Unknown error when deducing the layout.");
+    }
+  }
+  for (int i = std::min(a.ndim, b.ndim); i < a.ndim; i++) {
+    res.shape[dim - i - 1] = a.shape[a.ndim - i - 1];
+  }
+  for (int i = std::min(a.ndim, b.ndim); i < b.ndim; i++) {
+    res.shape[dim - i - 1] = b.shape[b.ndim - i - 1];
+  }
+  res.dtype = DType::from_enum(
+      deduce_double_input_op(a.dtype.enumv(), b.dtype.enumv()));
+  res.ndim = dim;
+  a.broadcast_inplace(res);
+  b.broadcast_inplace(res);
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/matmul.cpp b/core/op/common/matmul.cpp
index d67f46f..8c9a919 100644
--- a/core/op/common/matmul.cpp
+++ b/core/op/common/matmul.cpp
@@ -4,7 +4,8 @@ namespace nncore {
 namespace opr {
 
 IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(matmul) {
-  res.dtype = a.dtype;
+  res.dtype = DType::from_enum(
+      deduce_double_input_op(a.dtype.enumv(), b.dtype.enumv()));
   if (!a.ndim || !b.ndim)
     return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
                   "Encountered empty tensor during deduce.");
@@ -14,8 +15,8 @@ IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(matmul) {
     return Status::OK();
   }
   if (a.ndim == 1 && b.ndim == 1) {
-    res.shape[0] = b.shape[0];
-    res.shape[1] = a.shape[0];
+    res.shape[0] = a.shape[0];
+    res.shape[1] = b.shape[0];
     res.ndim = 2;
     a.broadcast_inplace({a.shape[0], 1});
     b.broadcast_inplace({1, b.shape[0]});
@@ -23,44 +24,52 @@ IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(matmul) {
   }
   nn_size dim = a.ndim > b.ndim ? a.ndim : b.ndim;
   res.ndim = dim;
-  std::vector<nn_size> a_dst_shape;
-  std::vector<nn_size> b_dst_shape;
-  for (nn_size i = dim - 1; i >= 2; i--) {
-    if (i >= a.ndim || i < b.ndim && a.shape[i] == 1) {
-      res.shape[i] = b.shape[i];
-    } else if (i >= b.ndim || i < a.ndim && b.shape[i] == 1) {
-      res.shape[i] = a.shape[i];
-    } else if (a.shape[i] == b.shape[i]) {
-      res.shape[i] = a.shape[i];
+  Shape a_dst_shape(a);
+  Shape b_dst_shape(b);
+
+  for (nn_size i = 0; i < dim - 2; i++) {
+    int a_idx = static_cast<int>(a.ndim - i - 3);
+    int b_idx = static_cast<int>(b.ndim - i - 3);
+    int target_idx = dim - i - 3;
+    if (a_idx >= 0 &&
+        (b_idx < 0 || a.shape[a_idx] == 1 || b.shape[b_idx] == 1 ||
+         a.shape[a_idx] == b.shape[b_idx])) {
+      if (b_idx < 0)
+        res.shape[target_idx] = a.shape[a_idx];
+      else
+        res.shape[target_idx] =
+            a.shape[a_idx] == 1 ? b.shape[b_idx] : a.shape[a_idx];
+    } else if (a_idx < 0) {
+      res.shape[target_idx] = b.shape[b_idx];
     } else {
       return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
                     "Tensor shapes mismatched for matmul, a is " +
                         a.Shape::to_string() + ", b is " +
                         b.Shape::to_string());
     }
-    a_dst_shape.push_back(res.shape[i]);
-    b_dst_shape.push_back(res.shape[i]);
+    a_dst_shape.shape[target_idx] = res.shape[target_idx];
+    b_dst_shape.shape[target_idx] = res.shape[target_idx];
   }
-  if (a.ndim == 1 && b.shape[1] != a.shape[0] ||
-      b.ndim == 1 && b.shape[0] != a.shape[0] ||
-      a.ndim != 1 && b.ndim != 1 && a.shape[0] != b.shape[1]) {
+  if (a.ndim == 1 && b.shape[b.ndim - 2] != a.shape[0] ||
+      b.ndim == 1 && b.shape[0] != a.shape[a.ndim - 1] ||
+      a.ndim != 1 && b.ndim != 1 &&
+          a.shape[a.ndim - 1] != b.shape[b.ndim - 2]) {
     return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
                   "Tensor shapes mismatched for matmul, a is " +
                       a.Shape::to_string() + ", b is " + b.Shape::to_string());
   }
-  res.shape[1] = a.ndim == 1 ? 1 : a.shape[1];
-  res.shape[0] = b.ndim == 1 ? 1 : b.shape[0];
-  a_dst_shape.push_back(a.ndim > 1 ? a.shape[1] : 1);
-  b_dst_shape.push_back(b.ndim > 1 ? b.shape[1] : b.shape[0]);
-  a_dst_shape.push_back(a.shape[0]);
-  b_dst_shape.push_back(b.ndim > 1 ? b.shape[0] : 1);
+  res.shape[dim - 2] = a_dst_shape[dim - 2] =
+      a.ndim == 1 ? 1 : a.shape[a.ndim - 2];
+  res.shape[dim - 1] = b_dst_shape[dim - 1] =
+      b.ndim == 1 ? 1 : b.shape[b.ndim - 1];
+  a_dst_shape[dim - 1] = b_dst_shape[dim - 2] = a.shape[a.ndim - 1];
   if (b.ndim == 1) {
-    b[1] = b[0];
-    b[0] = 1;
+    b.shape[1] = 1;
     b.ndim = 2;
-    b.stride[1] = 1;
-    b.stride[0] = 0;
+    b.stride[0] = 1;
+    b.stride[1] = 0;
   }
+  a_dst_shape.ndim = b_dst_shape.ndim = res.ndim;
   a.broadcast_inplace(a_dst_shape);
   b.broadcast_inplace(b_dst_shape);
   return Status::OK();
diff --git a/core/op/common/matrix_inverse.cpp b/core/op/common/matrix_inverse.cpp
new file mode 100644
index 0000000..ec59954
--- /dev/null
+++ b/core/op/common/matrix_inverse.cpp
@@ -0,0 +1,35 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(matrix_inverse) {
+  if (inp.ndim < 2) {
+    return Status(
+        StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+        "The tensor to calculate inverse must has at least two dims.");
+  }
+  if (inp.shape[inp.ndim - 1] != inp.shape[inp.ndim - 2]) {
+    return Status(
+        StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+        "The tensor to calculate inverse must has its last two dims square.");
+  }
+  if (!inp.is_contiguous()) {
+    return Status(StatusCategory::NUMNET, StatusCode::FAIL,
+                  "The tensor to calculate inverse must be contiguous.");
+  }
+  if (inp.dtype.enumv() != DTypeEnum::Float32 &&
+      inp.dtype.enumv() != DTypeEnum::Float64) {
+    return Status(StatusCategory::NUMNET, StatusCode::FAIL,
+                  "The inverse op only support float and double type.");
+  }
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/max.cpp b/core/op/common/max.cpp
new file mode 100644
index 0000000..b1b3e5a
--- /dev/null
+++ b/core/op/common/max.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(max) {
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    if (param.dims[i]) {
+      res.shape[i] = 1;
+    } else {
+      res.shape[i] = inp.shape[i];
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/mean.cpp b/core/op/common/mean.cpp
new file mode 100644
index 0000000..eef5dba
--- /dev/null
+++ b/core/op/common/mean.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(mean) {
+  res.dtype = dtype::Float64();
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    if (param.dims[i]) {
+      res.shape[i] = 1;
+    } else {
+      res.shape[i] = inp.shape[i];
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/min.cpp b/core/op/common/min.cpp
new file mode 100644
index 0000000..41e1f87
--- /dev/null
+++ b/core/op/common/min.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(min) {
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    if (param.dims[i]) {
+      res.shape[i] = 1;
+    } else {
+      res.shape[i] = inp.shape[i];
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/negative.cpp b/core/op/common/negative.cpp
new file mode 100644
index 0000000..423cc5d
--- /dev/null
+++ b/core/op/common/negative.cpp
@@ -0,0 +1,16 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(negative) {
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/onehot.cpp b/core/op/common/onehot.cpp
new file mode 100644
index 0000000..a10e479
--- /dev/null
+++ b/core/op/common/onehot.cpp
@@ -0,0 +1,17 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(onehot) {
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim + 1;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  res[res.ndim - 1] = param.max_val + 1;
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/ops.h b/core/op/common/ops.h
index e7da8b9..44a4626 100644
--- a/core/op/common/ops.h
+++ b/core/op/common/ops.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <string>
+#include <vector>
 
 #include "core/base/include/status.h"
 #include "core/base/include/tensor.h"
@@ -12,16 +13,26 @@ namespace opr {
 
 using namespace param;
 
-#define NN_FOREACH_SINGLE_INPUT_OP(cb) cb(reshape) cb(transpose) cb(permute)
+#define NN_FOREACH_SELF_MODIFY_OP(cb) \
+  cb(normal) cb(uniform) cb(eye) cb(fill) cb(linspace) cb(arange)
 
-#define NN_FOREACH_DOUBLE_INPUT_OP(cb) cb(matmul) cb(dot)
+#define NN_FOREACH_SINGLE_INPUT_OP(cb)                                        \
+  cb(transpose) cb(permute) cb(repeat) cb(flip) cb(matrix_inverse) cb(rotate) \
+      cb(pad) cb(sort) cb(onehot) cb(sum) cb(max) cb(min) cb(negative)
+
+#define NN_FOREACH_DOUBLE_INPUT_OP(cb) \
+  cb(matmul) cb(dot) cb(boolindex) cb(interelem)
 
 #define NN_FOREACH_SINGLE_INPUT_OP_WITH_PARAM(cb, ...) \
-  cb(reshape, __VA_ARGS__) cb(transpose, __VA_ARGS__)
+  cb(permute, __VA_ARGS__) cb(transpose, __VA_ARGS__)
 
 #define NN_FOREACH_DOUBLE_INPUT_OP_WITH_PARAM(cb, ...) \
   cb(matmul, __VA_ARGS__) cb(dot, __VA_ARGS__)
 
+#define DEF_OP_SELF_MODIFY(_name) \
+ public:                          \
+  virtual Status _name(Tensor& t, const param::_name& param) = 0;
+
 #define DEF_OP_SINGLE_INPUT(_name)                       \
  public:                                                 \
   virtual Status _name(const Tensor& inp, Tensor& oup,   \
@@ -29,7 +40,7 @@ using namespace param;
                                                          \
  protected:                                              \
   Status deduce_layout_##_name(Layout& inp, Layout& res, \
-                               const param::_name param);
+                               const param::_name& param);
 
 #define DEF_OP_DOUBLE_INPUT(_name)                                    \
  public:                                                              \
@@ -38,26 +49,82 @@ using namespace param;
                                                                       \
  protected:                                                           \
   Status deduce_layout_##_name(Layout& a, Layout& b, Layout& res,     \
-                               const param::_name param);
+                               const param::_name& param);
 
 class OpBase {
+  NN_FOREACH_SELF_MODIFY_OP(DEF_OP_SELF_MODIFY)
+
   NN_FOREACH_SINGLE_INPUT_OP(DEF_OP_SINGLE_INPUT)
 
   NN_FOREACH_DOUBLE_INPUT_OP(DEF_OP_DOUBLE_INPUT)
 
+  // The convert op is a single input op but has type convert.
+  // So we specially defined it manually here.
+ public:
+  virtual Status convert(const Tensor& inp, Tensor& oup,
+                         const param::convert& param) = 0;
+
+ protected:
+  Status deduce_layout_convert(Layout& inp, Layout& res,
+                               const param::convert& param);
+
+ public:
+  virtual Status argmxx(const Tensor& inp, Tensor& oup,
+                        const param::argmxx& param) = 0;
+
+ protected:
+  Status deduce_layout_argmxx(Layout& inp, Layout& res,
+                              const param::argmxx& param);
+
+ public:
+  virtual Status mean(const Tensor& inp, Tensor& oup,
+                      const param::mean& param) = 0;
+
+ protected:
+  Status deduce_layout_mean(Layout& inp, Layout& res, const param::mean& param);
+
+ public:
+  virtual Status concat(const std::vector<const Tensor*>& inp, Tensor& oup,
+                        const param::concat& param) = 0;
+
+ protected:
+  Status deduce_layout_concat(const std::vector<const Tensor*>& inp,
+                              Layout& res, const param::concat& param);
+
   virtual ~OpBase() = default;
 };
 
 #undef DEF_OP_SINGLE_INPUT
 #undef DEF_OP_DOUBLE_INPUT
 
-#define IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(_name)                   \
-  Status OpBase::deduce_layout_##_name(Layout& inp, Layout& res, \
-                                       const param::_name param)
-#define IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(_name)                            \
-  Status OpBase::deduce_layout_##_name(Layout& a, Layout& b, Layout& res, \
-                                       const param::_name param)
-
+#define IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(_name) \
+  Status OpBase::deduce_layout_##_name(        \
+      Layout& inp, Layout& res, [[maybe_unused]] const param::_name& param)
+#define IMPL_DOUBLE_INPUT_LAYOUT_DEDUCE(_name) \
+  Status OpBase::deduce_layout_##_name(        \
+      Layout& a, Layout& b, Layout& res,       \
+      [[maybe_unused]] const param::_name& param)
+
+#define IMPL_OP_SELF_MODIFY(_name)                                         \
+ public:                                                                   \
+  Status _name(Tensor& t, const param::_name& param) {                     \
+    if (!t.is_mutable()) {                                                 \
+      return Status(                                                       \
+          StatusCategory::NUMNET, StatusCode::RUNTIME_EXCEPTION,           \
+          "Self-modify op could only be used to create the Tensor, which " \
+          "means the current tensor should be mutable.");                  \
+    }                                                                      \
+    NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_SELF_MODIFY, _name)            \
+    return Status::OK();                                                   \
+  }                                                                        \
+                                                                           \
+  template <typename T>                                                    \
+  Status _name##_internal(T* ptr, const Layout& layout,                    \
+                          const param::_name& param);
+
+// If the oup tensor is not the owner of the memory, we cannot deduce and
+// relayout it. The responsibility of layout deduce belongs to the user. The
+// main user is csharp api.
 #define IMPL_OP_SINGLE_INPUT(_name)                                            \
  public:                                                                       \
   Status _name(const Tensor& inp, Tensor& oup, const param::_name& param) {    \
diff --git a/core/op/common/pad.cpp b/core/op/common/pad.cpp
new file mode 100644
index 0000000..84a8ac3
--- /dev/null
+++ b/core/op/common/pad.cpp
@@ -0,0 +1,24 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(pad) {
+  if (!param.size) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "The size of width must be larger than 0.");
+  }
+  if (param.size != inp.ndim * 2) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "The dims to pad must have length 2 * ndim.");
+  }
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i] + param.width[i * 2] + param.width[i * 2 + 1];
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/param.h b/core/op/common/param.h
index 17afc9b..dc268cb 100644
--- a/core/op/common/param.h
+++ b/core/op/common/param.h
@@ -7,10 +7,6 @@
 namespace nncore {
 namespace param {
 
-struct reshape {
-  Shape new_shape;
-};
-
 struct transpose {
   nn_size dimA;
   nn_size dimB;
@@ -19,16 +15,274 @@ struct transpose {
 };
 
 struct permute {
-  nn_size* dims;
+  nn_size *dims;
 
-  permute(const std::vector<nn_size>& value) {
+  permute(const std::vector<nn_size> &value) {
+    dims = (nn_size *)malloc(sizeof(nn_size) * NN_MAX_NDIM);
     memcpy(dims, value.data(), value.size() * sizeof(nn_size));
   }
+
+  ~permute() {
+    free(dims);
+    dims = nullptr;
+  }
+
+ private:
+  permute(const permute &) {}
+  void operator=(const permute &) {}
+};
+
+struct repeat {
+  int repeats;
+  int axis;
+
+  repeat(int repeats, int axis) : repeats(repeats), axis(axis) {}
+};
+
+struct flip {
+  nn_byte *dims;
+
+  flip(const std::vector<nn_byte> &value) {
+    dims = (nn_byte *)malloc(sizeof(nn_byte) * NN_MAX_NDIM);
+    memcpy(dims, value.data(), value.size() * sizeof(nn_byte));
+  }
+
+  ~flip() {
+    free(dims);
+    dims = nullptr;
+  }
+
+ private:
+  flip(const flip &) {}
+  void operator=(const flip &) {}
+};
+
+struct matrix_inverse {};
+
+struct rotate {
+  int k;
+  int dimA;
+  int dimB;
+
+  rotate(int k, int dimA, int dimB) : k(k), dimA(dimA), dimB(dimB) {
+    if (dimA > dimB) std::swap(dimA, dimB);
+  }
+};
+
+struct pad {
+  enum Mode : int32_t {
+    Constant = 1,
+    Edge = 2,
+    Maximum = 3,
+    Minimum = 4,
+    Medium = 5,
+    Mean = 6
+  };
+
+  Mode mode;
+  nn_size size;
+  nn_size *width;
+  double *constants;
+
+  pad(Mode mode, nn_size size, const std::vector<nn_size> &width,
+      const std::vector<double> &constants)
+      : mode(mode), size(size) {
+    this->width = (nn_size *)malloc(sizeof(nn_size) * width.size());
+    memcpy(this->width, width.data(), width.size() * sizeof(nn_size));
+    this->constants = (double *)malloc(sizeof(double) * constants.size());
+    memcpy(this->constants, constants.data(),
+           constants.size() * sizeof(double));
+  };
+
+  ~pad() {
+    if (width != nullptr) {
+      free(width);
+      width = nullptr;
+    }
+    if (constants != nullptr) {
+      free(constants);
+      constants = nullptr;
+    }
+  }
+
+ private:
+  pad(const pad &) {}
+  void operator=(const pad &) {}
+};
+
+struct sort {
+  enum Order : int32_t { Increase = 0, Decrease = 1 };
+
+  int axis;
+  Order order;
+
+  sort(int axis, Order order) : axis(axis), order(order) {}
+};
+
+struct arange {
+  double start;
+  double stop;
+  double step;
+
+  arange(int start, int stop, int step)
+      : start(start), stop(stop), step(step) {}
+};
+
+struct onehot {
+  int max_val;
+
+  onehot(int max_val) : max_val(max_val) {}
+};
+
+struct sum {
+  nn_byte *dims;
+
+  sum(const std::vector<nn_byte> &value) {
+    dims = (nn_byte *)malloc(sizeof(nn_byte) * NN_MAX_NDIM);
+    memcpy(dims, value.data(), value.size() * sizeof(nn_byte));
+  }
+
+  ~sum() {
+    free(dims);
+    dims = nullptr;
+  }
+
+ private:
+  sum(const sum &) {}
+  void operator=(const sum &) {}
+};
+
+struct mean {
+  nn_byte *dims;
+
+  mean(const std::vector<nn_byte> &value) {
+    dims = (nn_byte *)malloc(sizeof(nn_byte) * NN_MAX_NDIM);
+    memcpy(dims, value.data(), value.size() * sizeof(nn_byte));
+  }
+
+  ~mean() {
+    free(dims);
+    dims = nullptr;
+  }
+
+ private:
+  mean(const mean &) {}
+  void operator=(const mean &) {}
+};
+
+struct max {
+  nn_byte *dims;
+
+  max(const std::vector<nn_byte> &value) {
+    dims = (nn_byte *)malloc(sizeof(nn_byte) * NN_MAX_NDIM);
+    memcpy(dims, value.data(), value.size() * sizeof(nn_byte));
+  }
+
+  ~max() {
+    free(dims);
+    dims = nullptr;
+  }
+
+ private:
+  max(const max &) {}
+  void operator=(const max &) {}
+};
+
+struct min {
+  nn_byte *dims;
+
+  min(const std::vector<nn_byte> &value) {
+    dims = (nn_byte *)malloc(sizeof(nn_byte) * NN_MAX_NDIM);
+    memcpy(dims, value.data(), value.size() * sizeof(nn_byte));
+  }
+
+  ~min() {
+    free(dims);
+    dims = nullptr;
+  }
+
+ private:
+  min(const min &) {}
+  void operator=(const min &) {}
+};
+
+struct negative {};
+
+struct interelem {
+  enum Operation {
+    Add = 1,
+    Sub = 2,
+    Mul = 3,
+    Div = 4,
+    Mod = 5,
+    And = 6,
+    Or = 7,
+    Xor = 8
+  };
+
+  Operation op;
+
+  interelem(Operation op) : op(op) {}
+};
+
+struct argmxx {
+  int axis;
+  bool is_max;
+
+  argmxx(int axis, bool is_max) : axis(axis), is_max(is_max) {}
+};
+
+struct convert {
+  DTypeEnum target_type;
+
+  convert(DTypeEnum target_type) : target_type(target_type) {}
+};
+
+struct normal {
+  double avg;
+  double std;
+
+  normal(double avg, double std) : avg(avg), std(std) {}
+};
+
+struct uniform {
+  double min_value;
+  double max_value;
+  uniform(double min_value, double max_value)
+      : min_value(min_value), max_value(max_value) {}
+};
+
+struct eye {
+  int k;
+  eye(int k) : k(k) {}
+};
+
+struct fill {
+  double value;
+  fill(double value) : value(value) {}
+};
+
+struct linspace {
+  double start;
+  double stop;
+  int num;
+  bool is_endpoint;
+
+  linspace(double start, double stop, int num, bool is_endpoint)
+      : start(start), stop(stop), num(num), is_endpoint(is_endpoint) {}
+};
+
+struct concat {
+  int axis;
+
+  concat(int axis) : axis(axis) {}
 };
 
 struct matmul {};
 
 struct dot {};
+
+struct boolindex {};
 }  // namespace param
 
 }  // namespace nncore
diff --git a/core/op/common/permute.cpp b/core/op/common/permute.cpp
index d894c11..e9f348a 100644
--- a/core/op/common/permute.cpp
+++ b/core/op/common/permute.cpp
@@ -14,12 +14,12 @@ IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(permute) {
       return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
                     "Invalid permute param.");
     }
-    res[param.dims[i]] = inp[i];
+    res[i] = inp[param.dims[i]];
     if (duplicated & (1 << param.dims[i])) {
       return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
                     "Duplicated index in permute param.");
     }
-    duplicated += 1 << param.dims[i];
+    duplicated |= 1 << param.dims[i];
   }
   return Status::OK();
 }
diff --git a/core/op/common/repeats.cpp b/core/op/common/repeats.cpp
new file mode 100644
index 0000000..2fa9db7
--- /dev/null
+++ b/core/op/common/repeats.cpp
@@ -0,0 +1,25 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(repeat) {
+  if (param.axis < 0 || param.axis >= inp.ndim) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "Invalid repeat axis param.");
+  }
+  if (param.repeats <= 0) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "The repeat count must be positive number.");
+  }
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  res[param.axis] = inp[param.axis] * param.repeats;
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/reshape.cpp b/core/op/common/reshape.cpp
deleted file mode 100644
index e6e1c74..0000000
--- a/core/op/common/reshape.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "core/op/common/ops.h"
-
-namespace nncore {
-namespace opr {
-
-IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(reshape) {
-  return Status(StatusCategory::NUMNET, StatusCode::NOT_IMPLEMENTED);
-}
-
-}  // namespace opr
-}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/rotate.cpp b/core/op/common/rotate.cpp
new file mode 100644
index 0000000..e6bed47
--- /dev/null
+++ b/core/op/common/rotate.cpp
@@ -0,0 +1,36 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(rotate) {
+  if (param.k <= 0 || param.k > 3) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "The k must be between 1 and 3.");
+  }
+  if (param.dimA == param.dimB) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "The dimA and dimB cannot be the same.");
+  }
+  if (inp.ndim <= 1) {
+    return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+                  "The tensor to be rotated must have at least two dims.");
+  }
+  if (param.dimA < 0 || param.dimB < 0 || param.dimA >= inp.ndim ||
+      param.dimB >= inp.ndim) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "The dimA or dimB exceeds the valid range.");
+  }
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  if (param.k != 2) {
+    std::swap(res[param.dimA], res[param.dimB]);
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/sort.cpp b/core/op/common/sort.cpp
new file mode 100644
index 0000000..53f4551
--- /dev/null
+++ b/core/op/common/sort.cpp
@@ -0,0 +1,20 @@
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(sort) {
+  if (param.axis < 0 || param.axis >= inp.ndim) {
+    return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
+                  "Invalid axis param for sort op.");
+  }
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    res[i] = inp[i];
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/sum.cpp b/core/op/common/sum.cpp
new file mode 100644
index 0000000..b48b489
--- /dev/null
+++ b/core/op/common/sum.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+
+#include "core/op/common/ops.h"
+
+namespace nncore {
+namespace opr {
+
+IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(sum) {
+  res.dtype = inp.dtype;
+  res.ndim = inp.ndim;
+  for (nn_size i = 0; i < inp.ndim; i++) {
+    if (param.dims[i]) {
+      res.shape[i] = 1;
+    } else {
+      res.shape[i] = inp.shape[i];
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace opr
+}  // namespace nncore
\ No newline at end of file
diff --git a/core/op/common/transpose.cpp b/core/op/common/transpose.cpp
index 94a3e44..63b8ea7 100644
--- a/core/op/common/transpose.cpp
+++ b/core/op/common/transpose.cpp
@@ -6,15 +6,15 @@ namespace opr {
 IMPL_SINGLE_INPUT_LAYOUT_DEDUCE(transpose) {
   if (param.dimA >= inp.ndim || param.dimB >= inp.ndim) {
     return Status(StatusCategory::NUMNET, StatusCode::INVALID_PARAM,
-                  "Invalid permute param.");
+                  "Invalid transpose param.");
   }
   res.dtype = inp.dtype;
   res.ndim = inp.ndim;
   for (nn_size i = 0; i < inp.ndim; i++) {
     res[i] = inp[i];
   }
-  res[inp.ndim - param.dimA - 1] = inp[inp.ndim - param.dimB - 1];
-  res[inp.ndim - param.dimB - 1] = inp[inp.ndim - param.dimA - 1];
+  res[param.dimA] = inp[param.dimB];
+  res[param.dimB] = inp[param.dimA];
   return Status::OK();
 }
 
diff --git a/core/op/naive/arange.cpp b/core/op/naive/arange.cpp
new file mode 100644
index 0000000..eefd3cf
--- /dev/null
+++ b/core/op/naive/arange.cpp
@@ -0,0 +1,20 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SELF_MODIFY_INTERNAL(arange) {
+  nn_size n = layout.total_elems();
+  auto value = param.start;
+  for (nn_size i = 0; i < n; ++i) {
+    t[i] = static_cast<T>(value);
+    value += param.step;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/argmxx.cpp b/core/op/naive/argmxx.cpp
new file mode 100644
index 0000000..c4c9c53
--- /dev/null
+++ b/core/op/naive/argmxx.cpp
@@ -0,0 +1,53 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_SPECIFIED_TYPE_INTERNAL(argmxx, nn_int64) {
+  nn_size n = loup.total_elems();
+  nn_size src_idx[NN_MAX_NDIM];
+  nn_size idx_offset[NN_MAX_NDIM];
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    src_idx[i] = 0;
+    idx_offset[i] = (i + 1) == param.axis ? 2 : 1;
+  }
+  src_idx[!param.axis] = -1;
+
+  auto increase_idx = [&]() {
+    src_idx[!param.axis]++;  // if axis = 0, increase src_idx[1]
+    for (nn_size i = !param.axis; i < linp.ndim; i += idx_offset[i]) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + idx_offset[i]]++;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  for (nn_size i = 0; i < n; i++) {
+    auto base_offset = increase_idx();
+    T value = ptr_inp[base_offset];
+    nn_int64 idx = 0;
+
+    for (nn_size j = 1; j < linp.shape[param.axis]; j++) {
+      base_offset += linp.stride[param.axis];
+      if (ptr_inp[base_offset] > value && param.is_max ||
+          ptr_inp[base_offset] < value && !param.is_max) {
+        value = ptr_inp[base_offset];
+        idx = j;
+      }
+    }
+
+    int temp = src_idx[param.axis];
+    src_idx[param.axis] = 0;
+    ptr_oup[loup.indices_to_offset(src_idx)] = idx;
+    src_idx[param.axis] = temp;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/boolindex.cpp b/core/op/naive/boolindex.cpp
new file mode 100644
index 0000000..8c2981f
--- /dev/null
+++ b/core/op/naive/boolindex.cpp
@@ -0,0 +1,21 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+IMPL_NAIVE_DOUBLE_INPUT_INTERNAL(boolindex) {
+  nn_size n = loup.total_elems();
+  nn_size target_shape[4];
+  for (nn_size i = 0; i < n; i++) {
+    loup.offset_to_indices(i, target_shape);
+    auto bool_offset = lb.indices_to_offset(target_shape);
+    auto src_offset = la.indices_to_offset(target_shape);
+    ptr_oup[i] = ptr_b[bool_offset] ? ptr_a[src_offset] : static_cast<TA>(0);
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/concat.cpp b/core/op/naive/concat.cpp
new file mode 100644
index 0000000..c5badf4
--- /dev/null
+++ b/core/op/naive/concat.cpp
@@ -0,0 +1,50 @@
+#include <iostream>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+#define SPECIFY_CONCAT_OP_INTERNAL(_type, _class_name)       \
+  template Status _class_name::concat_internal<_type>(       \
+      const std::vector<const Tensor*>& inp, _type* ptr_oup, \
+      const Layout& loup, const param::concat& param);
+
+NN_FOREACH_CTYPE_WITH_PARAM(SPECIFY_CONCAT_OP_INTERNAL, OpNaiveImpl)
+
+template <typename T>
+Status OpNaiveImpl::concat_internal(const std::vector<const Tensor*>& inp,
+                                    T* oup, const Layout& loup,
+                                    const param::concat& param) {
+  nn_size inp_count = static_cast<nn_size>(inp.size());
+  auto find_src = [&](nn_size& target_index, size_t& src_index) {
+    for (nn_size i = 0; i < inp_count; i++) {
+      target_index -= inp[i]->layout.shape[param.axis];
+      if (target_index < 0) {
+        target_index += inp[i]->layout.shape[param.axis];
+        src_index = i;
+        return Status::OK();
+      }
+    }
+    return Status(StatusCategory::NUMNET, StatusCode::RUNTIME_EXCEPTION,
+                  "Unexpected behaviour in cancot op.");
+  };
+
+  nn_size n = loup.total_elems();
+  nn_size target_shape[4];
+  for (nn_size i = 0; i < n; i++) {
+    loup.offset_to_indices(i, target_shape);
+    size_t src_index;
+    nn_return_status_if_error(find_src(target_shape[param.axis], src_index));
+    oup[i] =
+        inp[src_index]
+            ->ptr<T>()[inp[src_index]->layout.indices_to_offset(target_shape)];
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/convert.cpp b/core/op/naive/convert.cpp
new file mode 100644
index 0000000..a87c0e8
--- /dev/null
+++ b/core/op/naive/convert.cpp
@@ -0,0 +1,43 @@
+#include <iostream>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+FOREACH_DOUBLE_INPUT_TYPE_PAIR(SPECIFY_CONVERT_OP_INTERNAL, OpNaiveImpl)
+
+template <typename TA, typename TB>
+Status OpNaiveImpl::convert_internal(const TA* inp, TB* oup, const Layout& linp,
+                                     const Layout& loup,
+                                     const param::convert& param) {
+  nn_size idx_offset = loup.ndim - NN_MAX_NDIM;
+  for (nn_size n = 0; n < (idx_offset == 0 ? loup[idx_offset] : 1); n++) {
+    nn_size n_offset_inp = n * linp.stride[idx_offset];
+    nn_size n_offset_oup = n * loup.stride[idx_offset];
+    for (nn_size c = 0; c < (idx_offset >= -1 ? loup[idx_offset + 1] : 1);
+         c++) {
+      nn_size nc_offset_inp = c * linp.stride[idx_offset + 1] + n_offset_inp;
+      nn_size nc_offset_oup = c * loup.stride[idx_offset + 1] + n_offset_oup;
+      for (nn_size i = 0; i < (idx_offset >= -2 ? loup[idx_offset + 2] : 1);
+           i++) {
+        nn_size nch_offset_inp =
+            i * linp.stride[idx_offset + 2] + nc_offset_inp;
+        nn_size nch_offset_oup =
+            i * loup.stride[idx_offset + 2] + nc_offset_oup;
+        for (nn_size j = 0; j < loup[idx_offset + 3]; j++) {
+          nn_size inp_pos = nch_offset_inp + j * linp.stride[idx_offset + 3];
+          nn_size oup_pos = nch_offset_oup + j * loup.stride[idx_offset + 3];
+          oup[oup_pos] = static_cast<TB>(inp[inp_pos]);
+        }
+      }
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/dot.cpp b/core/op/naive/dot.cpp
index 0ab8a92..93f9156 100644
--- a/core/op/naive/dot.cpp
+++ b/core/op/naive/dot.cpp
@@ -3,8 +3,34 @@
 namespace nncore {
 namespace opr {
 namespace naive {
+
 IMPL_NAIVE_DOUBLE_INPUT_INTERNAL(dot) {
-  return Status(StatusCategory::NUMNET, StatusCode::NOT_IMPLEMENTED);
+  nn_size idx_offset = loup.ndim - NN_MAX_NDIM;
+  for (nn_size n = 0; n < (idx_offset == 0 ? loup[idx_offset] : 1); n++) {
+    nn_size n_offset_a = n * la.stride[idx_offset];
+    nn_size n_offset_b = n * lb.stride[idx_offset];
+    nn_size n_offset_oup = n * loup.stride[idx_offset];
+    for (nn_size c = 0; c < (idx_offset >= -1 ? loup[idx_offset + 1] : 1);
+         c++) {
+      nn_size nc_offset_a = c * la.stride[idx_offset + 1] + n_offset_a;
+      nn_size nc_offset_b = c * lb.stride[idx_offset + 1] + n_offset_b;
+      nn_size nc_offset_oup = c * loup.stride[idx_offset + 1] + n_offset_oup;
+      for (nn_size i = 0; i < (idx_offset >= -2 ? loup[idx_offset + 2] : 1);
+           i++) {
+        nn_size nch_offset_a = i * la.stride[idx_offset + 2] + nc_offset_a;
+        nn_size nch_offset_b = i * lb.stride[idx_offset + 2] + nc_offset_b;
+        nn_size nch_offset_oup =
+            i * loup.stride[idx_offset + 2] + nc_offset_oup;
+        for (nn_size j = 0; j < loup[idx_offset + 3]; j++) {
+          nn_size a_pos = nch_offset_a + j * la.stride[idx_offset + 3];
+          nn_size b_pos = nch_offset_b + j * lb.stride[idx_offset + 3];
+          nn_size oup_pos = nch_offset_oup + j * loup.stride[idx_offset + 3];
+          ptr_oup[oup_pos] = static_cast<TC>(ptr_a[a_pos] * ptr_b[b_pos]);
+        }
+      }
+    }
+  }
+  return Status::OK();
 }
 
 }  // namespace naive
diff --git a/core/op/naive/eye.cpp b/core/op/naive/eye.cpp
new file mode 100644
index 0000000..41c179d
--- /dev/null
+++ b/core/op/naive/eye.cpp
@@ -0,0 +1,34 @@
+#include <cmath>
+#include <iostream>
+#include <random>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SELF_MODIFY_INTERNAL(eye) {
+  if (layout.ndim != 2) {
+    return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_SHAPE,
+                  "The eye could only be applied on  two-dimensional tensor.");
+  }
+  int m = layout.shape[0];
+  int n = layout.shape[1];
+  memset(t, 0, layout.total_elems() * layout.dtype.size());
+  //  i + k >= 0     i >= -k i >= 0
+  //  i + k < n      i < n-k i < m
+  int k = param.k;
+  int from = std::max(-k, 0);
+  int to = std::min(n - k, m);
+  for (int i = from; i < to; i++) {
+    int j = i + k;
+    t[i * n + j] = 1;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/fill.cpp b/core/op/naive/fill.cpp
new file mode 100644
index 0000000..b4179a6
--- /dev/null
+++ b/core/op/naive/fill.cpp
@@ -0,0 +1,22 @@
+#include <cmath>
+#include <iostream>
+#include <random>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SELF_MODIFY_INTERNAL(fill) {
+  nn_size n = layout.total_elems();
+  for (nn_size i = 0; i < n; i++) {
+    t[i] = static_cast<T>(param.value);
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/flip.cpp b/core/op/naive/flip.cpp
new file mode 100644
index 0000000..8348c77
--- /dev/null
+++ b/core/op/naive/flip.cpp
@@ -0,0 +1,62 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(flip) {
+  nn_size n = linp.total_elems();
+  nn_size src_idx[NN_MAX_NDIM];
+  memset(src_idx, 0, sizeof src_idx);
+  src_idx[0] = -1;
+
+  auto increase_idx = [&]() {
+    src_idx[0]++;
+    for (nn_size i = 0; i < linp.ndim; i++) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + 1]++;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  auto increase_without_axis = [&](nn_size axis) {
+    src_idx[!axis]++;  // if axis = 0, increase src_idx[1]
+    for (nn_size i = !axis; i < linp.ndim; i += (i + 1) == axis ? 2 : 1) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + ((i + 1) == axis ? 2 : 1)]++;
+      }
+    }
+    return loup.indices_to_offset(src_idx);
+  };
+
+  for (nn_size i = 0; i < n; i++) {
+    auto src_offset = increase_idx();
+    auto dst_offset = loup.indices_to_offset(src_idx);
+    ptr_oup[dst_offset] = ptr_inp[src_offset];
+  }
+
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) continue;
+    auto cnt = n / loup.shape[i];
+    memset(src_idx, 0, sizeof src_idx);
+    src_idx[!i] = -1;
+    for (nn_size j = 0; j < cnt; j++) {
+      auto dst_offset = increase_without_axis(i);
+      for (nn_size k = 0; k < loup.shape[i] / 2; k++) {
+        std::swap(
+            ptr_oup[dst_offset + k * loup.stride[i]],
+            ptr_oup[dst_offset + (loup.shape[i] - k - 1) * loup.stride[i]]);
+      }
+    }
+  }
+
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/interelem.cpp b/core/op/naive/interelem.cpp
new file mode 100644
index 0000000..af573ee
--- /dev/null
+++ b/core/op/naive/interelem.cpp
@@ -0,0 +1,178 @@
+#include <functional>
+
+#include "core/op/naive/ops.h"
+
+using Param = nncore::param::interelem;
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+template <typename TA, typename TB, typename TC>
+TC interelem_add(TA a, TB b) {
+  return static_cast<TC>(a + b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_sub(TA a, TB b) {
+  return static_cast<TC>(a - b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_mul(TA a, TB b) {
+  return static_cast<TC>(a * b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_div(TA a, TB b) {
+  return static_cast<TC>(a / b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_mod(TA a, TB b) {
+  return static_cast<TC>(a % b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_and(TA a, TB b) {
+  return static_cast<TC>(a & b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_or(TA a, TB b) {
+  return static_cast<TC>(a | b);
+}
+
+template <typename TA, typename TB, typename TC>
+TC interelem_xor(TA a, TB b) {
+  return static_cast<TC>(a ^ b);
+}
+
+IMPL_NAIVE_DOUBLE_INPUT_INTERNAL(interelem) {
+  nn_size idx_offset = loup.ndim - NN_MAX_NDIM;
+  std::function<TC(TA, TB)> func;
+  switch (param.op) {
+    case Param::Add:
+      func = interelem_add<TA, TB, TC>;
+      break;
+    case Param::Sub:
+      func = interelem_sub<TA, TB, TC>;
+      break;
+    case Param::Mul:
+      func = interelem_mul<TA, TB, TC>;
+      break;
+    case Param::Div:
+      func = interelem_div<TA, TB, TC>;
+      break;
+    case Param::Mod:
+      if (la.dtype.enumv() == DTypeEnum::Float32 ||
+          la.dtype.enumv() == DTypeEnum::Float64 ||
+          lb.dtype.enumv() == DTypeEnum::Float32 ||
+          lb.dtype.enumv() == DTypeEnum::Float64) {
+        return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_DTYPE,
+                      "The mod operator only accepts int or long as input");
+      }
+      if (loup.dtype.enumv() == DTypeEnum::Int32) {
+        func = interelem_mod<nn_int32, nn_int32, nn_int32>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Int64) {
+        func = interelem_mod<nn_int64, nn_int64, nn_int64>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Bool) {
+        func = interelem_mod<bool, bool, bool>;
+      } else {
+        return Status(StatusCategory::SYSTEM, StatusCode::FAIL,
+                      "Unexpected exception in interelem.cpp.");
+      }
+      break;
+    case Param::And:
+      if (la.dtype.enumv() == DTypeEnum::Float32 ||
+          la.dtype.enumv() == DTypeEnum::Float64 ||
+          lb.dtype.enumv() == DTypeEnum::Float32 ||
+          lb.dtype.enumv() == DTypeEnum::Float64) {
+        return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_DTYPE,
+                      "The and operator only accepts int or long as input");
+      }
+      if (loup.dtype.enumv() == DTypeEnum::Int32) {
+        func = interelem_and<nn_int32, nn_int32, nn_int32>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Int64) {
+        func = interelem_and<nn_int64, nn_int64, nn_int64>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Bool) {
+        func = interelem_and<bool, bool, bool>;
+      } else {
+        return Status(StatusCategory::SYSTEM, StatusCode::FAIL,
+                      "Unexpected exception in interelem.cpp.");
+      }
+      break;
+    case Param::Or:
+      if (la.dtype.enumv() == DTypeEnum::Float32 ||
+          la.dtype.enumv() == DTypeEnum::Float64 ||
+          lb.dtype.enumv() == DTypeEnum::Float32 ||
+          lb.dtype.enumv() == DTypeEnum::Float64) {
+        return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_DTYPE,
+                      "The or operator only accepts int or long as input");
+      }
+      if (loup.dtype.enumv() == DTypeEnum::Int32) {
+        func = interelem_or<nn_int32, nn_int32, nn_int32>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Int64) {
+        func = interelem_or<nn_int64, nn_int64, nn_int64>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Bool) {
+        func = interelem_or<bool, bool, bool>;
+      } else {
+        return Status(StatusCategory::SYSTEM, StatusCode::FAIL,
+                      "Unexpected exception in interelem.cpp.");
+      }
+      break;
+    case Param::Xor:
+      if (la.dtype.enumv() == DTypeEnum::Float32 ||
+          la.dtype.enumv() == DTypeEnum::Float64 ||
+          lb.dtype.enumv() == DTypeEnum::Float32 ||
+          lb.dtype.enumv() == DTypeEnum::Float64) {
+        return Status(StatusCategory::NUMNET, StatusCode::MISMATCHED_DTYPE,
+                      "The xor operator only accepts int or long as input");
+      }
+      if (loup.dtype.enumv() == DTypeEnum::Int32) {
+        func = interelem_xor<nn_int32, nn_int32, nn_int32>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Int64) {
+        func = interelem_xor<nn_int64, nn_int64, nn_int64>;
+      } else if (loup.dtype.enumv() == DTypeEnum::Bool) {
+        func = interelem_xor<bool, bool, bool>;
+      } else {
+        return Status(StatusCategory::SYSTEM, StatusCode::FAIL,
+                      "Unexpected exception in interelem.cpp.");
+      }
+      break;
+
+    default:
+      return Status(StatusCategory::NUMNET, StatusCode::FAIL,
+                    "Invalid types in interelem op.");
+  }
+  for (nn_size n = 0; n < (idx_offset == 0 ? loup[idx_offset] : 1); n++) {
+    nn_size n_offset_a = n * la.stride[idx_offset];
+    nn_size n_offset_b = n * lb.stride[idx_offset];
+    nn_size n_offset_oup = n * loup.stride[idx_offset];
+    for (nn_size c = 0; c < (idx_offset >= -1 ? loup[idx_offset + 1] : 1);
+         c++) {
+      nn_size nc_offset_a = c * la.stride[idx_offset + 1] + n_offset_a;
+      nn_size nc_offset_b = c * lb.stride[idx_offset + 1] + n_offset_b;
+      nn_size nc_offset_oup = c * loup.stride[idx_offset + 1] + n_offset_oup;
+      for (nn_size i = 0; i < (idx_offset >= -2 ? loup[idx_offset + 2] : 1);
+           i++) {
+        nn_size nch_offset_a = i * la.stride[idx_offset + 2] + nc_offset_a;
+        nn_size nch_offset_b = i * lb.stride[idx_offset + 2] + nc_offset_b;
+        nn_size nch_offset_oup =
+            i * loup.stride[idx_offset + 2] + nc_offset_oup;
+        for (nn_size j = 0; j < loup[idx_offset + 3]; j++) {
+          nn_size a_pos = nch_offset_a + j * la.stride[idx_offset + 3];
+          nn_size b_pos = nch_offset_b + j * lb.stride[idx_offset + 3];
+          nn_size oup_pos = nch_offset_oup + j * loup.stride[idx_offset + 3];
+          ptr_oup[oup_pos] = func(ptr_a[a_pos], ptr_b[b_pos]);
+        }
+      }
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/linspace.cpp b/core/op/naive/linspace.cpp
new file mode 100644
index 0000000..34642a7
--- /dev/null
+++ b/core/op/naive/linspace.cpp
@@ -0,0 +1,24 @@
+#include <cmath>
+#include <iostream>
+#include <random>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SELF_MODIFY_INTERNAL(linspace) {
+  nn_size n = layout.total_elems();
+  auto step = (param.stop - param.start) /
+              std::max(static_cast<double>(param.is_endpoint ? n - 1 : n), 1.0);
+  for (nn_size i = 0; i < n; ++i) {
+    t[i] = static_cast<T>(param.start + i * step);
+  }
+  return Status::OK();
+}
+
+} // namespace naive
+} // namespace opr
+
+} // namespace nncore
diff --git a/core/op/naive/matmul.cpp b/core/op/naive/matmul.cpp
index c2af923..6377743 100644
--- a/core/op/naive/matmul.cpp
+++ b/core/op/naive/matmul.cpp
@@ -7,22 +7,37 @@ namespace naive {
 IMPL_NAIVE_DOUBLE_INPUT_INTERNAL(matmul) {
   nn_size n_total = 1;
   nn_size c_total = 1;
-  if (loup.ndim >= 4) n_total = loup[3];
-  if (loup.ndim >= 3) c_total = loup[2];
+  nn_size n_idx = 0;
+  nn_size c_idx = loup.ndim == 4 ? 1 : 0;
+  nn_size pre_idx = la.ndim - 2;
+  nn_size a0 = la.ndim >= 2 ? la.shape[la.ndim - 2] : 1,
+          a1 = la.shape[la.ndim - 1];
+  nn_size b1 = lb.shape[lb.ndim - 1];
+  if (loup.ndim == 4) {
+    n_total = loup[n_idx];
+    c_total = loup[c_idx];
+  } else if (loup.ndim == 3)
+    c_total = loup[c_idx];
   for (nn_size n = 0; n < n_total; n++) {
+    nn_size n_offset_a = n * la.stride[n_idx];
+    nn_size n_offset_b = n * lb.stride[n_idx];
+    nn_size n_offset_oup = n * loup.stride[n_idx];
     for (nn_size c = 0; c < c_total; c++) {
-      for (nn_size i = 0; i < la[1]; i++) {
-        for (nn_size j = 0; j < lb[0]; j++) {
+      nn_size nc_offset_a = c * la.stride[c_idx] + n_offset_a;
+      nn_size nc_offset_b = c * lb.stride[c_idx] + n_offset_b;
+      nn_size nc_offset_oup = c * loup.stride[c_idx] + n_offset_oup;
+      for (nn_size i = 0; i < a0; i++) {
+        for (nn_size j = 0; j < b1; j++) {
           TC r = TC(0);
-          for (nn_size k = 0; k < la[0]; k++) {
-            nn_size a_pos =
-                n * la.stride[3] + c * la.stride[2] + i * la.stride[1] + k;
-            nn_size b_pos =
-                n * lb.stride[3] + c * lb.stride[2] + k * lb.stride[1] + j;
+          for (nn_size k = 0; k < a1; k++) {
+            nn_size a_pos = nc_offset_a + i * la.stride[pre_idx] +
+                            k * la.stride[pre_idx + 1];
+            nn_size b_pos = nc_offset_b + k * lb.stride[pre_idx] +
+                            j * lb.stride[pre_idx + 1];
             r += ptr_a[a_pos] * ptr_b[b_pos];
           }
-          nn_size oup_pos =
-              n * loup.stride[3] + c * loup.stride[2] + i * loup.stride[1] + j;
+          nn_size oup_pos = nc_offset_oup + i * loup.stride[pre_idx] +
+                            j * loup.stride[pre_idx + 1];
           ptr_oup[oup_pos] = r;
         }
       }
diff --git a/core/op/naive/matrix_inverse.cpp b/core/op/naive/matrix_inverse.cpp
new file mode 100644
index 0000000..ebae6f9
--- /dev/null
+++ b/core/op/naive/matrix_inverse.cpp
@@ -0,0 +1,89 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(matrix_inverse) {
+  if (linp.dtype.enumv() == DTypeEnum::Bool) {
+    return Status(StatusCategory::NUMNET, StatusCode::FAIL,
+                  "Cannot calculate inverse matrix for bool tensor.");
+  }
+  nn_size batch = 1;
+  for (nn_size i = 0; i < linp.ndim - 2; i++) {
+    batch *= linp.shape[i];
+  }
+
+  nn_size n = linp.shape[linp.ndim - 1];  // matrix scale
+
+  // alloc the extra space to help the calculation
+  T* rows[n];
+  for (nn_size i = 0; i < n; i++) {
+    // each row has 2n elements as I|X
+    rows[i] = (T*)malloc(sizeof(T) * 2 * n);
+  }
+
+  nn_size offset = 0;
+  for (nn_size b = 0; b < batch; b++, offset += n * n) {
+    for (nn_size i = 0; i < n; i++) {
+      memcpy(rows[i], ptr_inp + offset + i * n, sizeof(T) * n);
+      memset(rows[i] + n, 0, sizeof(T) * n);
+      rows[i][n + i] = static_cast<T>(1);
+    }
+    // loop on columns
+    for (nn_size j = 0; j < n; j++) {
+      nn_size main_elem;
+      T main_elem_val = static_cast<T>(0);
+      // find the main elem
+      for (int i = 0; i < n; i++) {
+        auto value = static_cast<T>(std::abs(rows[i][j]));
+        if (value > main_elem_val) {
+          main_elem = i;
+          main_elem_val = value;
+        }
+      }
+
+      if (main_elem_val < static_cast<T>(1e-7)) {
+        return Status(StatusCategory::NUMNET, StatusCode::FAIL,
+                      "Failed to calculate the inverse because the pivot value "
+                      "too small (smaller than 1e-7).");
+      }
+      std::swap(rows[j], rows[main_elem]);
+
+      // substract the current row from other rows
+      auto cur_row_ptr = rows[j];
+      main_elem_val = static_cast<T>(cur_row_ptr[j]);
+      for (nn_size i = 0; i < n; i++) {
+        if (i != j) {
+          auto weight = -rows[i][j] / main_elem_val;
+          for (nn_size k = j; k < 2 * n; k++) {
+            rows[i][k] += cur_row_ptr[k] * weight;
+          }
+        }
+      }
+
+      // normalize the current row
+      auto weight = static_cast<T>(1) / main_elem_val;
+      for (nn_size k = j; k < 2 * n; k++) {
+        cur_row_ptr[k] *= weight;
+      }
+    }
+
+    for (nn_size i = 0; i < n; i++) {
+      memcpy(ptr_oup + offset + i * n, rows[i] + n, sizeof(T) * n);
+    }
+  }
+
+  // free the alloced memory
+  for (nn_size i = 0; i < n; i++) {
+    free(rows[i]);
+    rows[i] = nullptr;
+  }
+
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/max.cpp b/core/op/naive/max.cpp
new file mode 100644
index 0000000..df9319e
--- /dev/null
+++ b/core/op/naive/max.cpp
@@ -0,0 +1,95 @@
+#include <iostream>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(max) {
+  nn_size n = 1;
+  nn_size src_idx[NN_MAX_NDIM], src_next[NN_MAX_NDIM],
+      src_internal_idx[NN_MAX_NDIM], src_next_reverse[NN_MAX_NDIM];
+  nn_size start_idx = 0;
+  nn_size start_idx_reverse = 0;
+  memset(src_idx, 0, sizeof src_idx);
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      start_idx = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (param.dims[i]) {
+      start_idx_reverse = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      src_next_reverse[i] = NN_MAX_NDIM;
+      src_next[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && param.dims[j]) {
+        src_next[i]++;
+        j++;
+      }
+      n *= linp.shape[i];
+    } else {
+      src_next[i] = NN_MAX_NDIM;
+      src_next_reverse[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && !param.dims[j]) {
+        src_next_reverse[i]++;
+        j++;
+      }
+    }
+  }
+  nn_size n_reverse = linp.total_elems() / n;
+
+  auto increase_idx = [&]() {
+    src_idx[start_idx]++;
+    for (nn_size i = start_idx; i < linp.ndim; i += src_next[i]) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + src_next[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  auto increase_idx_internal = [&]() {
+    src_internal_idx[start_idx_reverse]++;
+    for (nn_size i = start_idx_reverse; i < linp.ndim;
+         i += src_next_reverse[i]) {
+      if (src_internal_idx[i] == linp.shape[i]) {
+        src_internal_idx[i] = 0;
+        src_internal_idx[i + src_next_reverse[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_internal_idx);
+  };
+
+  src_idx[start_idx] = -1;
+  for (nn_size i = 0; i < n; i++) {
+    auto base_offset = increase_idx();
+    auto dst_offset = loup.indices_to_offset(src_idx);
+    T res = ptr_inp[base_offset];
+    memcpy(src_internal_idx, src_idx, sizeof(nn_size) * NN_MAX_NDIM);
+    src_internal_idx[start_idx_reverse] = -1;
+    for (nn_size j = 0; j < n_reverse; j++) {
+      res = std::max(res, ptr_inp[increase_idx_internal()]);
+    }
+    ptr_oup[dst_offset] = res;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/mean.cpp b/core/op/naive/mean.cpp
new file mode 100644
index 0000000..26f64fc
--- /dev/null
+++ b/core/op/naive/mean.cpp
@@ -0,0 +1,93 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_SPECIFIED_TYPE_INTERNAL(mean, nn_float64) {
+  nn_size n = 1;
+  nn_size src_idx[NN_MAX_NDIM], src_next[NN_MAX_NDIM],
+      src_internal_idx[NN_MAX_NDIM], src_next_reverse[NN_MAX_NDIM];
+  nn_size start_idx = 0;
+  nn_size start_idx_reverse = 0;
+  memset(src_idx, 0, sizeof src_idx);
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      start_idx = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (param.dims[i]) {
+      start_idx_reverse = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      src_next_reverse[i] = NN_MAX_NDIM;
+      src_next[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && param.dims[j]) {
+        src_next[i]++;
+        j++;
+      }
+      n *= linp.shape[i];
+    } else {
+      src_next[i] = NN_MAX_NDIM;
+      src_next_reverse[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && !param.dims[j]) {
+        src_next_reverse[i]++;
+        j++;
+      }
+    }
+  }
+  nn_size n_reverse = linp.total_elems() / n;
+
+  auto increase_idx = [&]() {
+    src_idx[start_idx]++;
+    for (nn_size i = start_idx; i < linp.ndim; i += src_next[i]) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + src_next[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  auto increase_idx_internal = [&]() {
+    src_internal_idx[start_idx_reverse]++;
+    for (nn_size i = start_idx_reverse; i < linp.ndim;
+         i += src_next_reverse[i]) {
+      if (src_internal_idx[i] == linp.shape[i]) {
+        src_internal_idx[i] = 0;
+        src_internal_idx[i + src_next_reverse[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_internal_idx);
+  };
+
+  src_idx[start_idx] = -1;
+  for (nn_size i = 0; i < n; i++) {
+    auto base_offset = increase_idx();
+    auto dst_offset = loup.indices_to_offset(src_idx);
+    nn_float64 res = 0;
+    memcpy(src_internal_idx, src_idx, sizeof(nn_size) * NN_MAX_NDIM);
+    src_internal_idx[start_idx_reverse] = -1;
+    for (nn_size j = 0; j < n_reverse; j++) {
+      res += static_cast<nn_float64>(ptr_inp[increase_idx_internal()]);
+    }
+    ptr_oup[dst_offset] = res / n_reverse;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/min.cpp b/core/op/naive/min.cpp
new file mode 100644
index 0000000..4492f6d
--- /dev/null
+++ b/core/op/naive/min.cpp
@@ -0,0 +1,95 @@
+#include <iostream>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(min) {
+  nn_size n = 1;
+  nn_size src_idx[NN_MAX_NDIM], src_next[NN_MAX_NDIM],
+      src_internal_idx[NN_MAX_NDIM], src_next_reverse[NN_MAX_NDIM];
+  nn_size start_idx = 0;
+  nn_size start_idx_reverse = 0;
+  memset(src_idx, 0, sizeof src_idx);
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      start_idx = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (param.dims[i]) {
+      start_idx_reverse = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      src_next_reverse[i] = NN_MAX_NDIM;
+      src_next[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && param.dims[j]) {
+        src_next[i]++;
+        j++;
+      }
+      n *= linp.shape[i];
+    } else {
+      src_next[i] = NN_MAX_NDIM;
+      src_next_reverse[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && !param.dims[j]) {
+        src_next_reverse[i]++;
+        j++;
+      }
+    }
+  }
+  nn_size n_reverse = linp.total_elems() / n;
+
+  auto increase_idx = [&]() {
+    src_idx[start_idx]++;
+    for (nn_size i = start_idx; i < linp.ndim; i += src_next[i]) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + src_next[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  auto increase_idx_internal = [&]() {
+    src_internal_idx[start_idx_reverse]++;
+    for (nn_size i = start_idx_reverse; i < linp.ndim;
+         i += src_next_reverse[i]) {
+      if (src_internal_idx[i] == linp.shape[i]) {
+        src_internal_idx[i] = 0;
+        src_internal_idx[i + src_next_reverse[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_internal_idx);
+  };
+
+  src_idx[start_idx] = -1;
+  for (nn_size i = 0; i < n; i++) {
+    auto base_offset = increase_idx();
+    auto dst_offset = loup.indices_to_offset(src_idx);
+    T res = ptr_inp[base_offset];
+    memcpy(src_internal_idx, src_idx, sizeof(nn_size) * NN_MAX_NDIM);
+    src_internal_idx[start_idx_reverse] = -1;
+    for (nn_size j = 0; j < n_reverse; j++) {
+      res = std::min(res, ptr_inp[increase_idx_internal()]);
+    }
+    ptr_oup[dst_offset] = res;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/negative.cpp b/core/op/naive/negative.cpp
new file mode 100644
index 0000000..eb79660
--- /dev/null
+++ b/core/op/naive/negative.cpp
@@ -0,0 +1,20 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(negative) {
+  nn_size n = loup.total_elems();
+  nn_size src_idx[NN_MAX_NDIM];
+  for (nn_size i = 0; i < n; i++) {
+    loup.offset_to_indices(i, src_idx);
+    ptr_oup[i] = -ptr_inp[linp.indices_to_offset(src_idx)];
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/normal.cpp b/core/op/naive/normal.cpp
new file mode 100644
index 0000000..5e4bf53
--- /dev/null
+++ b/core/op/naive/normal.cpp
@@ -0,0 +1,39 @@
+#include <cmath>
+#include <iostream>
+#include <random>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SELF_MODIFY_INTERNAL(normal) {
+  std::random_device rd;
+
+  std::mt19937 e2(rd());
+
+  std::normal_distribution<> dist(param.avg, param.std);
+  nn_size idx_offset = layout.ndim - NN_MAX_NDIM;
+  for (nn_size n = 0; n < (idx_offset == 0 ? layout[idx_offset] : 1); n++) {
+    nn_size n_pos = n * layout.stride[idx_offset];
+    for (nn_size c = 0; c < (idx_offset >= -1 ? layout[idx_offset + 1] : 1);
+         c++) {
+      nn_size nc_pos = c * layout.stride[idx_offset + 1] + n_pos;
+      for (nn_size i = 0; i < (idx_offset >= -2 ? layout[idx_offset + 2] : 1);
+           i++) {
+        nn_size nch_pos = i * layout.stride[idx_offset + 2] + nc_pos;
+        for (nn_size j = 0; j < layout[idx_offset + 3]; j++) {
+          t[nch_pos + j * layout.stride[idx_offset + 3]] =
+              static_cast<T>(dist(e2));
+        }
+      }
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/onehot.cpp b/core/op/naive/onehot.cpp
new file mode 100644
index 0000000..fca6b1c
--- /dev/null
+++ b/core/op/naive/onehot.cpp
@@ -0,0 +1,43 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(onehot) {
+  memset(ptr_oup, 0, sizeof(T) * loup.total_elems());
+  nn_size n = linp.total_elems();
+  nn_size src_idx[NN_MAX_NDIM];
+  memset(src_idx, 0, sizeof src_idx);
+  src_idx[0] = -1;
+
+  auto increase_idx = [&]() {
+    src_idx[0]++;
+    for (nn_size i = 0; i < linp.ndim; i++) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + 1]++;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  for (nn_size i = 0; i < n; i++) {
+    auto src_offset = increase_idx();
+    src_idx[loup.ndim - 1] = ptr_inp[src_offset];
+    if (src_idx[loup.ndim - 1] > param.max_val || src_idx[loup.ndim - 1] < 0) {
+      return Status(StatusCategory::NUMNET, StatusCode::FAIL,
+                    "Invalid data (" + std::to_string(src_idx[loup.ndim - 1]) +
+                        ") in onehot.");
+    }
+    auto dst_offset = loup.indices_to_offset(src_idx);
+    ptr_oup[dst_offset] = 1;
+  }
+
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/ops.h b/core/op/naive/ops.h
index 3d566e4..4d6a569 100644
--- a/core/op/naive/ops.h
+++ b/core/op/naive/ops.h
@@ -1,4 +1,6 @@
 #pragma once
+#include <iostream>
+
 #include "core/base/include/macro.h"
 #include "core/op/common/ops.h"
 
@@ -19,19 +21,122 @@ class OpNaiveImpl final : public OpBase {
     return &instance;
   }
 
+  NN_FOREACH_SELF_MODIFY_OP(IMPL_OP_SELF_MODIFY)
+
   NN_FOREACH_SINGLE_INPUT_OP(IMPL_OP_SINGLE_INPUT)
 
   NN_FOREACH_DOUBLE_INPUT_OP(IMPL_OP_DOUBLE_INPUT)
+
+  // Define the implementation of convert op specially
+ public:
+  Status convert(const Tensor& inp, Tensor& oup, const param::convert& param) {
+    Layout linp(inp.layout);
+    if (oup.is_ptr_owner()) {
+      Layout loup;
+      nn_return_status_if_error(deduce_layout_convert(linp, loup, param));
+      loup.init_contiguous_stride();
+      oup.relayout(loup);
+      TYPE_CONVERT_DEDUCE(linp.dtype.enumv(), loup.dtype.enumv(), linp, loup,
+                          param);
+    } else {
+      TYPE_CONVERT_DEDUCE(linp.dtype.enumv(), oup.layout.dtype.enumv(), linp,
+                          oup.layout, param);
+    }
+    return Status::OK();
+  }
+
+  template <typename TA, typename TB>
+  Status convert_internal(const TA* inp, TB* oup, const Layout& linp,
+                          const Layout& loup, const param::convert& param);
+
+ public:
+  Status argmxx(const Tensor& inp, Tensor& oup, const param::argmxx& param) {
+    Layout linp(inp.layout);
+    if (oup.is_ptr_owner()) {
+      Layout loup;
+      nn_return_status_if_error(deduce_layout_argmxx(linp, loup, param));
+      loup.init_contiguous_stride();
+      oup.relayout(loup);
+      NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_SINGLE_INPUT_SPECIFIED_TYPE,
+                                  argmxx, loup, nn_int64)
+    } else {
+      NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_SINGLE_INPUT_SPECIFIED_TYPE,
+                                  argmxx, oup.layout, nn_int64)
+    }
+    return Status::OK();
+  }
+
+  template <typename T>
+  Status argmxx_internal(const T* inp, nn_int64* oup, const Layout& linp,
+                         const Layout& loup, const param::argmxx& param);
+
+ public:
+  Status mean(const Tensor& inp, Tensor& oup, const param::mean& param) {
+    Layout linp(inp.layout);
+    if (oup.is_ptr_owner()) {
+      Layout loup;
+      nn_return_status_if_error(deduce_layout_mean(linp, loup, param));
+      loup.init_contiguous_stride();
+      oup.relayout(loup);
+      NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_SINGLE_INPUT_SPECIFIED_TYPE, mean,
+                                  loup, nn_float64)
+    } else {
+      NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_SINGLE_INPUT_SPECIFIED_TYPE, mean,
+                                  oup.layout, nn_float64)
+    }
+    return Status::OK();
+  }
+
+  template <typename T>
+  Status mean_internal(const T* inp, nn_float64* oup, const Layout& linp,
+                       const Layout& loup, const param::mean& param);
+
+ public:
+  Status concat(const std::vector<const Tensor*>& inp, Tensor& oup,
+                const param::concat& param) {
+    if (oup.is_ptr_owner()) {
+      Layout loup;
+      nn_return_status_if_error(deduce_layout_concat(inp, loup, param));
+      loup.init_contiguous_stride();
+      oup.relayout(loup);
+      NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_CONCAT, loup)
+    } else {
+      NN_FOREACH_CTYPE_WITH_PARAM(TYPE_SELECT_CONCAT, oup.layout)
+    }
+    return Status::OK();
+  }
+
+  template <typename T>
+  Status concat_internal(const std::vector<const Tensor*>& inp, T* oup,
+                         const Layout& loup, const param::concat& param);
 };
 
-#define IMPL_NAIVE_SINGLE_INPUT_INTERNAL(_name)                                \
-  NN_FOREACH_CTYPE_WITH_PARAM(SPECIFY_SINGLE_OUTPUT_OP_INTERNAL, OpNaiveImpl,  \
-                              _name)                                           \
-                                                                               \
-  template <typename T>                                                        \
-  Status OpNaiveImpl::_name##_internal(const T* ptr_inp, T* ptr_oup,           \
-                                       const Layout& linp, const Layout& loup, \
-                                       const param::_name& param)
+#define IMPL_NAIVE_SINGLE_INPUT_SPECIFIED_TYPE_INTERNAL(_name, _typeOup)    \
+  NN_FOREACH_CTYPE_WITH_PARAM(                                              \
+      SPECIFY_SINGLE_OUTPUT_SPECIFIED_TYPE_OP_INTERNAL, OpNaiveImpl, _name, \
+      _typeOup)                                                             \
+                                                                            \
+  template <typename T>                                                     \
+  Status OpNaiveImpl::_name##_internal(                                     \
+      const T* ptr_inp, _typeOup* ptr_oup, const Layout& linp,              \
+      const Layout& loup, [[maybe_unused]] const param::_name& param)
+
+#define IMPL_NAIVE_SELF_MODIFY_INTERNAL(_name)                              \
+  NN_FOREACH_CTYPE_WITH_PARAM(SPECIFY_SELF_MODIFY_OP_INTERNAL, OpNaiveImpl, \
+                              _name)                                        \
+                                                                            \
+  template <typename T>                                                     \
+  Status OpNaiveImpl::_name##_internal(                                     \
+      T* t, const Layout& layout, [[maybe_unused]] const param::_name& param)
+
+#define IMPL_NAIVE_SINGLE_INPUT_INTERNAL(_name)                               \
+  NN_FOREACH_CTYPE_WITH_PARAM(SPECIFY_SINGLE_OUTPUT_OP_INTERNAL, OpNaiveImpl, \
+                              _name)                                          \
+                                                                              \
+  template <typename T>                                                       \
+  Status OpNaiveImpl::_name##_internal(                                       \
+      const T* ptr_inp, T* ptr_oup, const Layout& linp, const Layout& loup,   \
+      [[maybe_unused]] const param::_name& param)
 
 #define IMPL_NAIVE_DOUBLE_INPUT_INTERNAL(_name)                        \
   FOREACH_DOUBLE_INPUT_TYPE_PAIR(SPECIFY_DOUBLE_OUTPUT_OP_INTERNAL,    \
@@ -40,9 +145,8 @@ class OpNaiveImpl final : public OpBase {
   template <typename TA, typename TB, typename TC>                     \
   Status OpNaiveImpl::_name##_internal(                                \
       const TA* ptr_a, const TB* ptr_b, TC* ptr_oup, const Layout& la, \
-      const Layout& lb, const Layout& loup, const param::_name& param)
-
-// NN_FOREACH_CTYPE_WITH_PARAM(EXPLICIT_DECLARE_TEMPLATE_CLASS, OpNaiveImpl)
+      const Layout& lb, const Layout& loup,                            \
+      [[maybe_unused]] const param::_name& param)
 
 }  // namespace naive
 }  // namespace opr
diff --git a/core/op/naive/pad.cpp b/core/op/naive/pad.cpp
new file mode 100644
index 0000000..6f834c3
--- /dev/null
+++ b/core/op/naive/pad.cpp
@@ -0,0 +1,290 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+using pad_param = nncore::param::pad;
+
+template <typename T>
+void exec_constant(const T* ptr_inp, T* ptr_oup, const Layout& linp,
+                   const Layout& loup, const param::pad& param) {
+  nn_size dst_idx[NN_MAX_NDIM], src_idx[NN_MAX_NDIM];
+  nn_size n = loup.total_elems();
+  memset(dst_idx, 0, sizeof dst_idx);
+  dst_idx[0] = -1;
+
+  for (nn_size i = 0; i < n; i++) {
+    dst_idx[0]++;
+    int flag = 0;
+    nn_size constant_idx = 0;
+    for (nn_size j = 0; j < loup.ndim; j++) {
+      if (dst_idx[j] == loup.shape[j]) {
+        dst_idx[j] = 0;
+        dst_idx[j + 1]++;
+      }
+
+      if (dst_idx[j] < param.width[j * 2]) {
+        flag = 1;
+        constant_idx = j;
+      } else if (dst_idx[j] >= linp.shape[j] + param.width[j * 2]) {
+        flag = 2;
+        constant_idx = j;
+      } else {
+        src_idx[j] = dst_idx[j] - param.width[j * 2];
+      }
+    }
+
+    if (!flag) {
+      ptr_oup[loup.indices_to_offset(dst_idx)] =
+          ptr_inp[linp.indices_to_offset(src_idx)];
+    } else if (flag == 1) {
+      ptr_oup[loup.indices_to_offset(dst_idx)] =
+          static_cast<T>(param.constants[constant_idx * 2]);
+    } else {
+      ptr_oup[loup.indices_to_offset(dst_idx)] =
+          static_cast<T>(param.constants[constant_idx * 2 + 1]);
+    }
+  }
+}
+
+template <typename T>
+void exec_edge(const T* ptr_inp, T* ptr_oup, const Layout& linp,
+               const Layout& loup, const param::pad& param) {
+  nn_size dst_idx[NN_MAX_NDIM], src_idx[NN_MAX_NDIM];
+  nn_size n = loup.total_elems();
+  memset(dst_idx, 0, sizeof dst_idx);
+  dst_idx[0] = -1;
+
+  for (nn_size i = 0; i < n; i++) {
+    dst_idx[0]++;
+    for (nn_size j = 0; j < loup.ndim; j++) {
+      if (dst_idx[j] == loup.shape[j]) {
+        dst_idx[j] = 0;
+        dst_idx[j + 1]++;
+      }
+
+      if (dst_idx[j] < param.width[j * 2]) {
+        src_idx[j] = 0;
+      } else if (dst_idx[j] >= linp.shape[j] + param.width[j * 2]) {
+        src_idx[j] = linp.shape[j] - 1;
+      } else {
+        src_idx[j] = dst_idx[j] - param.width[j * 2];
+      }
+    }
+
+    ptr_oup[loup.indices_to_offset(dst_idx)] =
+        ptr_inp[linp.indices_to_offset(src_idx)];
+  }
+}
+
+// This implementation is not the same with that of numpy.
+// The pad of the corner is not the max elements of its row
+// and column in the dst tensor. Instead, it's the max value of
+// the closest row and column of the src tensor.
+template <typename T>
+void exec_max(const T* ptr_inp, T* ptr_oup, const Layout& linp,
+              const Layout& loup, const param::pad& param) {
+  nn_size n_dst = loup.total_elems();
+  nn_size n_src = linp.total_elems();
+  nn_size dst_idx[NN_MAX_NDIM], src_idx[NN_MAX_NDIM];
+  bool modify[NN_MAX_NDIM];
+  T* refer[linp.ndim];
+
+  auto get_refer_offset = [&](nn_size ignore_axis) {
+    nn_size res = 0;
+    for (nn_size i = 0; i < linp.ndim; i++) {
+      if (i != ignore_axis) res = res * linp.shape[i] + src_idx[i];
+    }
+    return res;
+  };
+
+  // calculate the refers
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    auto cur_elems = n_src / linp.shape[i];
+    refer[i] = (T*)malloc(sizeof(T) * cur_elems);
+    for (nn_size j = 0; j < cur_elems; j++) {
+      refer[i][j] = DTypeTrait<T>::min();
+    }
+    for (nn_size m = 0; m < linp.shape[i]; m++) {
+      memset(src_idx, 0, sizeof src_idx);
+      src_idx[i] = m;
+      src_idx[!i] = -1;
+      for (nn_size j = 0; j < cur_elems; j++) {
+        src_idx[!i]++;
+        for (nn_size k = !i; k < linp.ndim; k++) {
+          if (src_idx[k] == linp.shape[k]) {
+            src_idx[k] = 0;
+            src_idx[k + 1 + (k + 1 == i)]++;
+          } else {
+            break;
+          }
+        }
+        auto temp = get_refer_offset(i);
+        refer[i][temp] =
+            std::max(refer[i][temp], ptr_inp[linp.indices_to_offset(src_idx)]);
+      }
+    }
+  }
+  memset(dst_idx, 0, sizeof dst_idx);
+  dst_idx[0] = -1;
+
+  // fill the values
+  for (nn_size i = 0; i < n_dst; i++) {
+    dst_idx[0]++;
+    memset(modify, false, sizeof(bool) * NN_MAX_NDIM);
+    for (nn_size j = 0; j < loup.ndim; j++) {
+      if (dst_idx[j] == loup.shape[j]) {
+        dst_idx[j] = 0;
+        dst_idx[j + 1]++;
+      }
+      if (dst_idx[j] < param.width[j * 2]) {
+        modify[j] = true;
+        src_idx[j] = 0;
+      } else if (dst_idx[j] >= linp.shape[j] + param.width[j * 2]) {
+        modify[j] = true;
+        src_idx[j] = linp.shape[j] - 1;
+      } else {
+        src_idx[j] = dst_idx[j] - param.width[j * 2];
+      }
+    }
+    T value = DTypeTrait<T>::min();
+    bool flag = true;
+    for (nn_size j = 0; j < loup.ndim; j++) {
+      if (modify[j]) {
+        flag = false;
+        value = std::max(value, refer[j][get_refer_offset(j)]);
+      }
+    }
+
+    if (flag) {
+      ptr_oup[loup.indices_to_offset(dst_idx)] =
+          ptr_inp[linp.indices_to_offset(src_idx)];
+    } else {
+      ptr_oup[loup.indices_to_offset(dst_idx)] = value;
+    }
+  }
+
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    free(refer[i]);
+    refer[i] = nullptr;
+  }
+}
+
+template <typename T>
+void exec_min(const T* ptr_inp, T* ptr_oup, const Layout& linp,
+              const Layout& loup, const param::pad& param) {
+  nn_size n_dst = loup.total_elems();
+  nn_size n_src = linp.total_elems();
+  nn_size dst_idx[NN_MAX_NDIM], src_idx[NN_MAX_NDIM];
+  bool modify[NN_MAX_NDIM];
+  T* refer[linp.ndim];
+
+  auto get_refer_offset = [&](nn_size ignore_axis) {
+    nn_size res = 0;
+    for (nn_size i = 0; i < linp.ndim; i++) {
+      if (i != ignore_axis) res = res * linp.shape[i] + src_idx[i];
+    }
+    return res;
+  };
+
+  // calculate the refers
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    auto cur_elems = n_src / linp.shape[i];
+    refer[i] = (T*)malloc(sizeof(T) * cur_elems);
+    for (nn_size j = 0; j < cur_elems; j++) {
+      refer[i][j] = DTypeTrait<T>::max();
+    }
+    for (nn_size m = 0; m < linp.shape[i]; m++) {
+      memset(src_idx, 0, sizeof src_idx);
+      src_idx[i] = m;
+      src_idx[!i] = -1;
+      for (nn_size j = 0; j < cur_elems; j++) {
+        src_idx[!i]++;
+        for (nn_size k = !i; k < linp.ndim; k++) {
+          if (src_idx[k] == linp.shape[k]) {
+            src_idx[k] = 0;
+            src_idx[k + 1 + (k + 1 == i)]++;
+          } else {
+            break;
+          }
+        }
+        auto temp = get_refer_offset(i);
+        refer[i][temp] =
+            std::min(refer[i][temp], ptr_inp[linp.indices_to_offset(src_idx)]);
+      }
+    }
+  }
+  memset(dst_idx, 0, sizeof dst_idx);
+  dst_idx[0] = -1;
+
+  // fill the values
+  for (nn_size i = 0; i < n_dst; i++) {
+    dst_idx[0]++;
+    memset(modify, false, sizeof(bool) * NN_MAX_NDIM);
+    for (nn_size j = 0; j < loup.ndim; j++) {
+      if (dst_idx[j] == loup.shape[j]) {
+        dst_idx[j] = 0;
+        dst_idx[j + 1]++;
+      }
+      if (dst_idx[j] < param.width[j * 2]) {
+        modify[j] = true;
+        src_idx[j] = 0;
+      } else if (dst_idx[j] >= linp.shape[j] + param.width[j * 2]) {
+        modify[j] = true;
+        src_idx[j] = linp.shape[j] - 1;
+      } else {
+        src_idx[j] = dst_idx[j] - param.width[j * 2];
+      }
+    }
+    T value = DTypeTrait<T>::max();
+    bool flag = true;
+    for (nn_size j = 0; j < loup.ndim; j++) {
+      if (modify[j]) {
+        flag = false;
+        value = std::min(value, refer[j][get_refer_offset(j)]);
+      }
+    }
+
+    if (flag) {
+      ptr_oup[loup.indices_to_offset(dst_idx)] =
+          ptr_inp[linp.indices_to_offset(src_idx)];
+    } else {
+      ptr_oup[loup.indices_to_offset(dst_idx)] = value;
+    }
+  }
+
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    free(refer[i]);
+    refer[i] = nullptr;
+  }
+}
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(pad) {
+  switch (param.mode) {
+    case pad_param::Mode::Constant:
+      exec_constant<T>(ptr_inp, ptr_oup, linp, loup, param);
+      break;
+    case pad_param::Mode::Edge:
+      exec_edge<T>(ptr_inp, ptr_oup, linp, loup, param);
+      break;
+    case pad_param::Mode::Maximum:
+      exec_max<T>(ptr_inp, ptr_oup, linp, loup, param);
+      break;
+    case pad_param::Mode::Minimum:
+      exec_min<T>(ptr_inp, ptr_oup, linp, loup, param);
+      break;
+
+    default:
+      return Status(StatusCategory::NUMNET, StatusCode::NOT_IMPLEMENTED,
+                    "This mode is not implemeted so far and will be "
+                    "implemented in the future version.");
+  }
+
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/permute.cpp b/core/op/naive/permute.cpp
index 184c45f..2cd8008 100644
--- a/core/op/naive/permute.cpp
+++ b/core/op/naive/permute.cpp
@@ -12,7 +12,7 @@ IMPL_NAIVE_SINGLE_INPUT_INTERNAL(permute) {
   for (nn_size i = 0; i < n; i++) {
     loup.offset_to_indices(i, dst_idx);
     for (int j = 0; j < loup.ndim; j++) {
-      src_idx[j] = dst_idx[param.dims[j]];
+      src_idx[param.dims[j]] = dst_idx[j];
     }
     nn_size src_pos = linp.indices_to_offset(src_idx);
     ptr_oup[i] = ptr_inp[src_pos];
diff --git a/core/op/naive/repeat.cpp b/core/op/naive/repeat.cpp
new file mode 100644
index 0000000..e278756
--- /dev/null
+++ b/core/op/naive/repeat.cpp
@@ -0,0 +1,46 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(repeat) {
+  nn_size n = linp.total_elems();
+  nn_size src_idx[NN_MAX_NDIM];
+  memset(src_idx, 0, sizeof src_idx);
+  src_idx[0] = -1;
+
+  auto increase_idx = [&]() {
+    src_idx[0]++;
+    for (nn_size i = 0; i < linp.ndim; i++) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + 1]++;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  for (nn_size i = 0; i < n; i++) {
+    auto src_offset = increase_idx();
+    T value = ptr_inp[src_offset];
+
+    auto temp = src_idx[param.axis];
+    src_idx[param.axis] *= param.repeats;
+
+    auto dst_offset = loup.indices_to_offset(src_idx);
+
+    for (nn_size j = 0; j < param.repeats; j++) {
+      ptr_oup[dst_offset] = value;
+      dst_offset += loup.stride[param.axis];
+    }
+
+    src_idx[param.axis] = temp;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/reshape.cpp b/core/op/naive/reshape.cpp
deleted file mode 100644
index fe58cb6..0000000
--- a/core/op/naive/reshape.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "core/op/naive/ops.h"
-
-namespace nncore {
-namespace opr {
-namespace naive {
-IMPL_NAIVE_SINGLE_INPUT_INTERNAL(reshape) {
-  return Status(StatusCategory::NUMNET, StatusCode::NOT_IMPLEMENTED);
-}
-
-}  // namespace naive
-}  // namespace opr
-
-}  // namespace nncore
diff --git a/core/op/naive/rotate.cpp b/core/op/naive/rotate.cpp
new file mode 100644
index 0000000..2efe1f4
--- /dev/null
+++ b/core/op/naive/rotate.cpp
@@ -0,0 +1,77 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(rotate) {
+  nn_size rotate_n = linp.shape[param.dimA] * linp.shape[param.dimB];
+  nn_size other_n = linp.total_elems() / rotate_n;
+  nn_size src_idx[NN_MAX_NDIM], src_next[NN_MAX_NDIM], dst_idx[NN_MAX_NDIM];
+  memset(src_idx, 0, sizeof src_idx);
+  nn_size start = NN_MAX_NDIM;
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (i == param.dimA || i == param.dimB) {
+      src_next[i] = NN_MAX_NDIM;
+      continue;
+    }
+    if (start == NN_MAX_NDIM) start = i;
+    if (i + 1 == param.dimA || i + 1 == param.dimB) {
+      if (i + 2 == param.dimA || i + 2 == param.dimB) {
+        src_next[i] = 3;
+      } else {
+        src_next[i] = 2;
+      }
+    } else {
+      src_next[i] = 1;
+    }
+  }
+
+  auto increase_idx = [&]() {
+    src_idx[start]++;
+    for (nn_size i = start; i < linp.ndim; i += src_next[i]) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + src_next[i]]++;
+      }
+    }
+  };
+
+  auto src_to_dst = [&]() {
+    if (param.k == 1) {
+      dst_idx[param.dimA] = src_idx[param.dimB];
+      dst_idx[param.dimB] = linp.shape[param.dimA] - src_idx[param.dimA] - 1;
+    } else if (param.k == 2) {
+      dst_idx[param.dimA] = linp.shape[param.dimA] - src_idx[param.dimA] - 1;
+      dst_idx[param.dimB] = linp.shape[param.dimB] - src_idx[param.dimB] - 1;
+    } else {
+      dst_idx[param.dimB] = src_idx[param.dimA];
+      dst_idx[param.dimA] = linp.shape[param.dimB] - src_idx[param.dimB] - 1;
+    }
+  };
+
+  if (start != NN_MAX_NDIM) {
+    src_idx[start] = -1;
+  }
+  for (nn_size i = 0; i < other_n; i++) {
+    increase_idx();
+    for (nn_size j = 0; j < linp.shape[param.dimA]; j++) {
+      src_idx[param.dimA] = j;
+      for (nn_size k = 0; k < linp.shape[param.dimB]; k++) {
+        src_idx[param.dimB] = k;
+        auto src_offset = linp.indices_to_offset(src_idx);
+        memcpy(dst_idx, src_idx, sizeof(nn_size) * NN_MAX_NDIM);
+        src_to_dst();
+        auto dst_offset = loup.indices_to_offset(dst_idx);
+        ptr_oup[dst_offset] = ptr_inp[src_offset];
+      }
+    }
+  }
+
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/sort.cpp b/core/op/naive/sort.cpp
new file mode 100644
index 0000000..e86542d
--- /dev/null
+++ b/core/op/naive/sort.cpp
@@ -0,0 +1,92 @@
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+template <typename T>
+void quick_sort_increase(nn_size base_offset, nn_size stride, nn_size l,
+                         nn_size r, T* ptr_oup) {
+  if (l >= r) return;
+  auto refer = ptr_oup[base_offset + (l + r + 1) / 2 * stride];
+  auto a = l - 1, b = r + 1;
+  while (a < b) {
+    a++;
+    while (ptr_oup[base_offset + a * stride] < refer) a++;
+    b--;
+    while (ptr_oup[base_offset + b * stride] > refer) b--;
+    if (a < b)
+      std::swap(ptr_oup[base_offset + a * stride],
+                ptr_oup[base_offset + b * stride]);
+  }
+  quick_sort_increase<T>(base_offset, stride, l, a - 1, ptr_oup);
+  quick_sort_increase<T>(base_offset, stride, a, r, ptr_oup);
+}
+
+template <typename T>
+void quick_sort_decrease(nn_size base_offset, nn_size stride, nn_size l,
+                         nn_size r, T* ptr_oup) {
+  if (l >= r) return;
+  auto refer = ptr_oup[base_offset + (l + r + 1) / 2 * stride];
+  auto a = l - 1, b = r + 1;
+  while (a < b) {
+    a++;
+    while (ptr_oup[base_offset + a * stride] > refer) a++;
+    b--;
+    while (ptr_oup[base_offset + b * stride] < refer) b--;
+    if (a < b)
+      std::swap(ptr_oup[base_offset + a * stride],
+                ptr_oup[base_offset + b * stride]);
+  }
+  quick_sort_decrease<T>(base_offset, stride, l, a - 1, ptr_oup);
+  quick_sort_decrease<T>(base_offset, stride, a, r, ptr_oup);
+}
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(sort) {
+  nn_size n = linp.total_elems();
+  nn_size src_idx[NN_MAX_NDIM];
+
+  // copy all the data to target tensor first
+  for (nn_size i = 0; i < n; i++) {
+    loup.offset_to_indices(i, src_idx);
+    ptr_oup[i] = ptr_inp[linp.indices_to_offset(src_idx)];
+  }
+
+  // quick sort
+  memset(src_idx, 0, sizeof src_idx);
+  src_idx[!param.axis] = -1;
+  auto other_elems = n / loup.shape[param.axis];
+  auto axis_shape = loup.shape[param.axis];
+  auto stride = loup.stride[param.axis];
+
+  auto increase_idx = [&]() {
+    src_idx[!param.axis]++;
+    for (nn_size i = !param.axis; i < linp.ndim; i++) {
+      if (i == param.axis) continue;
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + 1 + (i + 1 == param.axis)]++;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  if (param.order == param::sort::Order::Increase) {
+    for (auto i = 0; i < other_elems; i++) {
+      auto base_offset = increase_idx();
+      quick_sort_increase<T>(base_offset, stride, 0, axis_shape - 1, ptr_oup);
+    }
+  } else {
+    for (auto i = 0; i < other_elems; i++) {
+      auto base_offset = increase_idx();
+      quick_sort_decrease<T>(base_offset, stride, 0, axis_shape - 1, ptr_oup);
+    }
+  }
+
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/sum.cpp b/core/op/naive/sum.cpp
new file mode 100644
index 0000000..ea60443
--- /dev/null
+++ b/core/op/naive/sum.cpp
@@ -0,0 +1,95 @@
+#include <iostream>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SINGLE_INPUT_INTERNAL(sum) {
+  nn_size n = 1;
+  nn_size src_idx[NN_MAX_NDIM], src_next[NN_MAX_NDIM],
+      src_internal_idx[NN_MAX_NDIM], src_next_reverse[NN_MAX_NDIM];
+  nn_size start_idx = 0;
+  nn_size start_idx_reverse = 0;
+  memset(src_idx, 0, sizeof src_idx);
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      start_idx = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (param.dims[i]) {
+      start_idx_reverse = i;
+      break;
+    }
+  }
+  for (nn_size i = 0; i < linp.ndim; i++) {
+    if (!param.dims[i]) {
+      src_next_reverse[i] = NN_MAX_NDIM;
+      src_next[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && param.dims[j]) {
+        src_next[i]++;
+        j++;
+      }
+      n *= linp.shape[i];
+    } else {
+      src_next[i] = NN_MAX_NDIM;
+      src_next_reverse[i] = 1;
+      auto j = i + 1;
+      while (j < linp.ndim && !param.dims[j]) {
+        src_next_reverse[i]++;
+        j++;
+      }
+    }
+  }
+  nn_size n_reverse = linp.total_elems() / n;
+
+  auto increase_idx = [&]() {
+    src_idx[start_idx]++;
+    for (nn_size i = start_idx; i < linp.ndim; i += src_next[i]) {
+      if (src_idx[i] == linp.shape[i]) {
+        src_idx[i] = 0;
+        src_idx[i + src_next[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_idx);
+  };
+
+  auto increase_idx_internal = [&]() {
+    src_internal_idx[start_idx_reverse]++;
+    for (nn_size i = start_idx_reverse; i < linp.ndim;
+         i += src_next_reverse[i]) {
+      if (src_internal_idx[i] == linp.shape[i]) {
+        src_internal_idx[i] = 0;
+        src_internal_idx[i + src_next_reverse[i]]++;
+      } else {
+        break;
+      }
+    }
+    return linp.indices_to_offset(src_internal_idx);
+  };
+
+  src_idx[start_idx] = -1;
+  for (nn_size i = 0; i < n; i++) {
+    auto base_offset = increase_idx();
+    auto dst_offset = loup.indices_to_offset(src_idx);
+    T res = static_cast<T>(0);
+    memcpy(src_internal_idx, src_idx, sizeof(nn_size) * NN_MAX_NDIM);
+    src_internal_idx[start_idx_reverse] = -1;
+    for (nn_size j = 0; j < n_reverse; j++) {
+      res += ptr_inp[increase_idx_internal()];
+    }
+    ptr_oup[dst_offset] = res;
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/op/naive/transpose.cpp b/core/op/naive/transpose.cpp
index 04e5c37..8336258 100644
--- a/core/op/naive/transpose.cpp
+++ b/core/op/naive/transpose.cpp
@@ -12,8 +12,8 @@ IMPL_NAIVE_SINGLE_INPUT_INTERNAL(transpose) {
     for (nn_size j = 0; j < loup.ndim; j++) {
       src_idx[j] = dst_idx[j];
     }
-    src_idx[loup.ndim - param.dimA - 1] = dst_idx[loup.ndim - param.dimB - 1];
-    src_idx[loup.ndim - param.dimB - 1] = dst_idx[loup.ndim - param.dimA - 1];
+    src_idx[param.dimA] = dst_idx[param.dimB];
+    src_idx[param.dimB] = dst_idx[param.dimA];
     nn_size src_pos = linp.indices_to_offset(src_idx);
     ptr_oup[i] = ptr_inp[src_pos];
   }
diff --git a/core/op/naive/uniform.cpp b/core/op/naive/uniform.cpp
new file mode 100644
index 0000000..cc6b592
--- /dev/null
+++ b/core/op/naive/uniform.cpp
@@ -0,0 +1,57 @@
+#include <cmath>
+#include <iostream>
+#include <random>
+
+#include "core/op/naive/ops.h"
+
+namespace nncore {
+namespace opr {
+namespace naive {
+
+IMPL_NAIVE_SELF_MODIFY_INTERNAL(uniform) {
+  std::random_device rd;
+
+  std::mt19937 e2(rd());
+
+  std::uniform_real_distribution<> dist(param.min_value, param.max_value);
+  nn_size idx_offset = layout.ndim - NN_MAX_NDIM;
+  if (layout.dtype.is_ctype<nn_bool>()) {
+    for (nn_size n = 0; n < (idx_offset == 0 ? layout[idx_offset] : 1); n++) {
+      nn_size n_pos = n * layout.stride[idx_offset];
+      for (nn_size c = 0; c < (idx_offset >= -1 ? layout[idx_offset + 1] : 1);
+           c++) {
+        nn_size nc_pos = c * layout.stride[idx_offset + 1] + n_pos;
+        for (nn_size i = 0; i < (idx_offset >= -2 ? layout[idx_offset + 2] : 1);
+             i++) {
+          nn_size nch_pos = i * layout.stride[idx_offset + 2] + nc_pos;
+          for (nn_size j = 0; j < layout[idx_offset + 3]; j++) {
+            t[nch_pos + j * layout.stride[idx_offset + 3]] =
+                dist(e2) > 0.5 ? true : false;
+          }
+        }
+      }
+    }
+  } else {
+    for (nn_size n = 0; n < (idx_offset == 0 ? layout[idx_offset] : 1); n++) {
+      nn_size n_pos = n * layout.stride[idx_offset];
+      for (nn_size c = 0; c < (idx_offset >= -1 ? layout[idx_offset + 1] : 1);
+           c++) {
+        nn_size nc_pos = c * layout.stride[idx_offset + 1] + n_pos;
+        for (nn_size i = 0; i < (idx_offset >= -2 ? layout[idx_offset + 2] : 1);
+             i++) {
+          nn_size nch_pos = i * layout.stride[idx_offset + 2] + nc_pos;
+          for (nn_size j = 0; j < layout[idx_offset + 3]; j++) {
+            t[nch_pos + j * layout.stride[idx_offset + 3]] =
+                static_cast<T>(dist(e2));
+          }
+        }
+      }
+    }
+  }
+  return Status::OK();
+}
+
+}  // namespace naive
+}  // namespace opr
+
+}  // namespace nncore
diff --git a/core/test/CMakeLists.txt b/core/test/CMakeLists.txt
index 9ac324b..2bb7f54 100644
--- a/core/test/CMakeLists.txt
+++ b/core/test/CMakeLists.txt
@@ -1,24 +1,32 @@
 
 file(GLOB_RECURSE SOURCES common/*.cpp naive/*.cpp)
 
-# set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps")
-
 enable_testing()
 
-add_executable(
-  core_test
-  ${SOURCES}
-)
-
-target_link_libraries(
-core_test
-PUBLIC core_base
-PUBLIC core_op
-PRIVATE gtest_main
-)
-
-target_include_directories(core_test PUBLIC ${PROJECT_SOURCE_DIR})
+if(NOT WIN32 AND NN_CROSS_COMPILE)
+    add_executable( tensornet_core_test ${SOURCES} )
+    target_link_libraries(
+      tensornet_core_test
+      PRIVATE tensornet_core_base
+      PRIVATE tensornet_core_op
+      PRIVATE gtest_main
+    )
+    set_target_properties(tensornet_core_test PROPERTIES
+        SUFFIX "exe"
+        LINK_FLAGS "-Wl,--add-stdcall-alias"
+    )
+    target_include_directories(tensornet_core_test PRIVATE ${PROJECT_SOURCE_DIR})
+else()
+    add_executable(tensornet_core_test ${SOURCES})
+    target_link_libraries(
+      tensornet_core_test
+      PRIVATE tensornet_core_base
+      PRIVATE tensornet_core_op
+      PRIVATE gtest_main
+    )
+    target_include_directories(tensornet_core_test PRIVATE ${PROJECT_SOURCE_DIR})
+endif()
 
 include(GoogleTest)
-gtest_discover_tests(core_test)
+gtest_discover_tests(tensornet_core_test)
   
\ No newline at end of file
diff --git a/core/test/common/basic.cpp b/core/test/common/basic.cpp
index a06eaab..21ff354 100644
--- a/core/test/common/basic.cpp
+++ b/core/test/common/basic.cpp
@@ -1,4 +1,3 @@
-#include "core/base/include/tensor.h"
 #include "core/op/naive/ops.h"
 #include "core/test/common/factory.h"
 #include "core/test/common/utils.h"
diff --git a/core/test/common/factory.h b/core/test/common/factory.h
index 5c73c9d..e73a09e 100644
--- a/core/test/common/factory.h
+++ b/core/test/common/factory.h
@@ -17,7 +17,7 @@ class NDArrayFactory {
   static Tensor from_list(std::initializer_list<V> values, Shape shape,
                           T dtype) {
     Tensor res(shape, dtype);
-    nn_assert(values.size() == shape.total_elems(),
+    nn_assert(static_cast<nn_size>(values.size()) == shape.total_elems(),
               "The values used to init the Tensor is not compitable with the "
               "given shape. The element count of initial values is %ld, the "
               "shape is %s, which means %d elements.",
diff --git a/core/test/common/matmul.h b/core/test/common/matmul.h
index 465d062..f87afb5 100644
--- a/core/test/common/matmul.h
+++ b/core/test/common/matmul.h
@@ -12,7 +12,7 @@ struct TestArgs {
       : params(params), a(a), b(b), oup(oup) {}
 };
 
-inline std::vector<TestArgs> get_args() {}
+// inline std::vector<TestArgs> get_args() {}
 
 }  // namespace matmul
 
diff --git a/core/test/common/utils.h b/core/test/common/utils.h
index 164840b..296676b 100644
--- a/core/test/common/utils.h
+++ b/core/test/common/utils.h
@@ -14,8 +14,11 @@ void assert_same_data(const Tensor &a, const Tensor &b, float err = 0.000001f) {
   auto lptr = a.ptr<ctype>();
   auto rptr = b.ptr<ctype>();
   for (nn_size i = 0; i < a.layout.total_elems(); i++) {
-    // std::cout << "a: " << lptr[i] << " , b: " << rptr[i] << std::endl;
-    ASSERT_NEAR(lptr[i], rptr[i], err);
+    if (!a.layout.dtype.is_ctype<bool>() && !b.layout.dtype.is_ctype<bool>()) {
+      ASSERT_NEAR(lptr[i], rptr[i], err);
+    } else {
+      ASSERT_EQ(lptr[i], rptr[i]);
+    }
   }
 }
 
@@ -43,12 +46,25 @@ void assert_same_view(const Tensor &a, const Tensor &b, float err = 0.000001f) {
 
 template <typename ctype>
 void print_data(const Tensor &src) {
+  auto get_real_pos = [&src](int idx) {
+    int res = 0;
+    int mod = 1;
+    for (int i = src.layout.ndim - 1; i >= 1; i--) mod *= src.layout.shape[i];
+    for (int i = 0; i < src.layout.ndim; i++) {
+      int shape = idx / mod;
+      idx -= shape * mod;
+      res += shape * src.layout.stride[i];
+      if (i < src.layout.ndim - 1) mod /= src.layout.shape[i + 1];
+    }
+    return res;
+  };
+
   src.layout.dtype.assert_is_ctype<ctype>();
   nn_assert(!src.layout.is_empty(), "Cannot print an empty ndarray.");
   auto ptr = src.ptr<ctype>();
   for (nn_size i = 0; i < src.layout.total_elems(); i++) {
     nn_size mod = 1;
-    for (nn_size j = 0; j < src.layout.ndim; j++) {
+    for (nn_size j = src.layout.ndim - 1; j >= 0; j--) {
       mod *= src.layout.shape[j];
       if (i % mod == 0) {
         std::cout << "[";
@@ -58,13 +74,14 @@ void print_data(const Tensor &src) {
     }
     std::cout << " ";
 
-    std::cout << ptr[i];
-    if ((i + 1) % src.layout.shape[0] != 0) std::cout << ",";
+    std::cout << ptr[get_real_pos(i)];
+
+    if ((i + 1) % src.layout.shape[src.layout.ndim - 1] != 0) std::cout << ",";
 
     std::cout << " ";
     mod = 1;
     nn_size hit_times = 0;
-    for (nn_size j = 0; j < src.layout.ndim; j++) {
+    for (nn_size j = src.layout.ndim - 1; j >= 0; j--) {
       mod *= src.layout.shape[j];
       if ((i + 1) % mod == 0) {
         std::cout << "]";
@@ -75,7 +92,7 @@ void print_data(const Tensor &src) {
     }
     if (hit_times > 0 && hit_times < src.layout.ndim) {
       std::cout << "," << std::endl;
-      for (nn_size j = 0; j < hit_times; j++) {
+      for (nn_size j = 0; j < src.layout.ndim - hit_times; j++) {
         std::cout << " ";
       }
     }
diff --git a/core/test/naive/argmxx.cpp b/core/test/naive/argmxx.cpp
new file mode 100644
index 0000000..97b6e07
--- /dev/null
+++ b/core/test/naive/argmxx.cpp
@@ -0,0 +1,42 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::argmxx;
+
+TEST(Naive, Argmxx) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 =
+      F::from_list({33, 11, 13, 48, 14, 49, 14, 34, 43, 19, 13, 12, 0,  15, 29,
+                    1,  28, 6,  2,  20, 31, 39, 47, 34, 40, 12, 7,  7,  24, 23,
+                    20, 43, 30, 35, 45, 1,  15, 44, 30, 9,  25, 42, 21, 12, 41,
+                    41, 8,  45, 5,  43, 30, 42, 2,  13, 18, 46, 7,  34, 2,  20},
+                   {3, 4, 5}, dtype::Int32());
+  Tensor truth1 = F::from_list({1, 3, 1, 0, 2, 0, 2, 0, 2, 2, 3, 0, 1, 2, 1},
+                               {3, 1, 5}, dtype::Int64());
+  Param p1(1, true);
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->argmxx(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int64>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list({10, 9,  14, 14, 41, 36, 39, 17, 39, 23,
+                              48, 35, 44, 23, 45, 49, 42, 27, 23, 4},
+                             {4, 5}, dtype::Int32());
+  Tensor truth2 = F::from_list({3, 3, 2, 1, 2}, {1, 5}, dtype::Int64());
+  Param p2(0, true);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->argmxx(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_int64>(pred2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/boolindex.cpp b/core/test/naive/boolindex.cpp
new file mode 100644
index 0000000..ecd94be
--- /dev/null
+++ b/core/test/naive/boolindex.cpp
@@ -0,0 +1,30 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::boolindex;
+
+TEST(Naive, BoolIndex) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor a = F::from_list(
+      {39, 42, 16, 33, 44, 35, 16, 46, 0, 48, 3, 25, 12, 14, 25, 46, 42, 35},
+      {2, 3, 3}, dtype::Int32());
+  Tensor b = F::from_list({1, 0, 1, 0, 1, 0}, {2, 1, 3}, dtype::Bool());
+  Tensor truth = F::from_list(
+      {39, 0, 16, 33, 0, 35, 16, 0, 0, 0, 3, 0, 0, 14, 0, 0, 42, 0}, {2, 3, 3},
+      dtype::Int32());
+  Param p1;
+
+  Tensor pred;
+  ASSERT_TRUE(oprs->boolindex(a, b, pred, p1).is_ok());
+  assert_same_data<int>(pred, truth, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/concat.cpp b/core/test/naive/concat.cpp
new file mode 100644
index 0000000..3be1dac
--- /dev/null
+++ b/core/test/naive/concat.cpp
@@ -0,0 +1,36 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::concat;
+
+TEST(Naive, Concat) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor a = F::from_list(
+      {39, 42, 16, 33, 44, 35, 16, 46, 0, 48, 3, 25, 12, 14, 25, 46, 42, 35},
+      {2, 3, 3}, dtype::Int32());
+  Tensor b = F::from_list({6, 26, 0, 12, 7, 40, 41, 1, 19, 25, 10, 32},
+                          {2, 2, 3}, dtype::Int32());
+  Tensor c = F::from_list({37, 0,  0,  41, 9,  28, 47, 16, 4, 47, 15, 39,
+                           29, 12, 20, 12, 29, 0,  49, 40, 9, 48, 42, 6},
+                          {2, 4, 3}, dtype::Int32());
+  Tensor truth = F::from_list(
+      {39, 42, 16, 33, 44, 35, 16, 46, 0,  6,  26, 0,  12, 7,  40, 37, 0,  0,
+       41, 9,  28, 47, 16, 4,  47, 15, 39, 48, 3,  25, 12, 14, 25, 46, 42, 35,
+       41, 1,  19, 25, 10, 32, 29, 12, 20, 12, 29, 0,  49, 40, 9,  48, 42, 6},
+      {2, 9, 3}, dtype::Int32());
+  Param p1(1);
+
+  Tensor pred;
+  ASSERT_TRUE(oprs->concat({&a, &b, &c}, pred, p1).is_ok());
+  assert_same_data<int>(pred, truth, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/convert.cpp b/core/test/naive/convert.cpp
new file mode 100644
index 0000000..631a3db
--- /dev/null
+++ b/core/test/naive/convert.cpp
@@ -0,0 +1,54 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::convert;
+
+TEST(Naive, Convert) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 =
+      F::from_list({116,  0,   79,  0,   -28,  -96, -165, -27,  -2,  10,
+                    -176, 113, -30, 122, -116, -51, -48,  -142, 198, 0},
+                   {4, 5}, dtype::Int32());
+  Tensor truth1 = F::from_list(
+      {true, false, true, false, true, true, true, true, true, true,
+       true, true,  true, true,  true, true, true, true, true, false},
+      {4, 5}, dtype::Bool());
+  Param p1(DTypeEnum::Bool);
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->convert(inp1, pred1, p1).is_ok());
+  assert_same_data<bool>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list(
+      {-149, 141, 6,    -5,   47,   -183, 95,  53,   -149, -129, 186,  99,
+       21,   84,  -14,  175,  -53,  62,   140, 59,   -14,  50,   -169, -189,
+       61,   -7,  129,  -125, 120,  -109, 118, 162,  -80,  58,   -91,  115,
+       127,  173, -140, -95,  0,    192,  66,  -83,  -188, -29,  -92,  -145,
+       -6,   6,   -149, 159,  155,  93,   -74, -147, -37,  182,  123,  -49,
+       112,  -23, -157, -73,  -182, -120, -45, 31,   -119, -2,   18,   -199},
+      {3, 4, 6}, dtype::Int32());
+  Tensor truth2 = F::from_list(
+      {-149, 141, 6,    -5,   47,   -183, 95,  53,   -149, -129, 186,  99,
+       21,   84,  -14,  175,  -53,  62,   140, 59,   -14,  50,   -169, -189,
+       61,   -7,  129,  -125, 120,  -109, 118, 162,  -80,  58,   -91,  115,
+       127,  173, -140, -95,  0,    192,  66,  -83,  -188, -29,  -92,  -145,
+       -6,   6,   -149, 159,  155,  93,   -74, -147, -37,  182,  123,  -49,
+       112,  -23, -157, -73,  -182, -120, -45, 31,   -119, -2,   18,   -199},
+      {3, 4, 6}, dtype::Float64());
+  Param p2(DTypeEnum::Float64);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->convert(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_float64>(pred2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/dot.cpp b/core/test/naive/dot.cpp
new file mode 100644
index 0000000..6c0df12
--- /dev/null
+++ b/core/test/naive/dot.cpp
@@ -0,0 +1,51 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::dot;
+
+TEST(Naive, Dot) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor a1 = F::from_list({1, 2, 3, 4, 5, 6}, {2, 3}, dtype::Int32());
+  Tensor b1 = F::from_list({-1, 1, 2, 1, -1, 3}, {2, 3}, dtype::Int32());
+  Tensor truth1 = F::from_list({-1, 2, 6, 4, -5, 18}, {2, 3}, dtype::Int32());
+  Param p1;
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->dot(a1, b1, pred1, p1).is_ok());
+  assert_same_data<int>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor a2 = F::from_list({1, 3, 5, 7, 9, 0}, {2, 3}, dtype::Int32());
+  Tensor b2 = F::from_list({1, 2, 3}, {3}, dtype::Int32());
+  Tensor truth2 = F::from_list({1, 6, 15, 7, 18, 0}, {2, 3}, dtype::Int32());
+  Param p2;
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->dot(a2, b2, pred2, p2).is_ok());
+  assert_same_data<int>(pred2, truth2, 0.0001f);
+
+  // Group 4
+  Tensor a4 = F::from_list({1, 2, 5, -2, -4, 6, 1, 2, 5, -2, -4, 6,
+                            1, 2, 5, -2, -4, 6, 1, 2, 5, -2, -4, 6},
+                           {1, 4, 2, 3}, dtype::Int32());
+  Tensor b4 = F::from_list({1, 1, 2, 3, -2, -4, 8, 15, -7, -1, 5, 0},
+                           {1, 4, 1, 3}, dtype::Int32());
+  Tensor truth4 =
+      F::from_list({1, 2,  10,  -2,  -4,  12,  3,  -4, -20, -6, 8,   -24,
+                    8, 30, -35, -16, -60, -42, -1, 10, 0,   2,  -20, 0},
+                   {1, 4, 2, 3}, dtype::Int32());
+  Param p4;
+
+  Tensor pred4;
+  ASSERT_TRUE(oprs->dot(a4, b4, pred4, p4).is_ok());
+  assert_same_data<int>(pred4, truth4, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/eye.cpp b/core/test/naive/eye.cpp
new file mode 100644
index 0000000..389c5c0
--- /dev/null
+++ b/core/test/naive/eye.cpp
@@ -0,0 +1,46 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::eye;
+
+TEST(Naive, Eye) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor t1 =
+      F::from_list({116,  -50, 79,  -180, -28,  -96, -165, -27,  -2,  10,
+                    -176, 113, -30, 122,  -116, -51, -48,  -142, 198, -108},
+                   {4, 5}, dtype::Int32());
+  Tensor truth1 =
+      F::from_list({0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1},
+                   {4, 5}, dtype::Int32());
+  Param p1(1);
+
+  ASSERT_TRUE(oprs->eye(t1, p1).is_ok());
+  assert_same_data<nn_int32>(t1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor t2 =
+      F::from_list({-149, 141, 6,    -5,   47,  -183, 95,  53,   -149, -129,
+                    186,  99,  21,   84,   -14, 175,  -53, 62,   140,  59,
+                    -14,  50,  -169, -189, 61,  -7,   129, -125, 120,  -109,
+                    118,  162, -80,  58,   -91, 115,  127, 173,  -140, -95},
+                   {8, 5}, dtype::Int32());
+  Tensor truth2 =
+      F::from_list({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
+                    0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
+                   {8, 5}, dtype::Int32());
+  Param p2(-2);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->eye(t2, p2).is_ok());
+  assert_same_data<nn_int32>(t2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/fill.cpp b/core/test/naive/fill.cpp
new file mode 100644
index 0000000..76318be
--- /dev/null
+++ b/core/test/naive/fill.cpp
@@ -0,0 +1,37 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::fill;
+
+TEST(Naive, Fill) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor t1({2, 3}, dtype::Int32());
+  Tensor truth1 =
+      F::from_list({123, 123, 123, 123, 123, 123}, {2, 3}, dtype::Int32());
+  Param p1(123);
+
+  ASSERT_TRUE(oprs->fill(t1, p1).is_ok());
+  assert_same_data<nn_int32>(t1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor t2({3, 2, 1, 4}, dtype::Float64());
+  Tensor truth2 = F::from_list(
+      {1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52,
+       1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52, 1.52},
+      {3, 2, 1, 4}, dtype::Float64());
+  Param p2(1.52);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->fill(t2, p2).is_ok());
+  assert_same_data<nn_float64>(t2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/flip.cpp b/core/test/naive/flip.cpp
new file mode 100644
index 0000000..49b7dcf
--- /dev/null
+++ b/core/test/naive/flip.cpp
@@ -0,0 +1,67 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::flip;
+
+TEST(Naive, Flip) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list({48, 43, 48, 0,  49, 4,  17, 40, 29, 16,
+                              16, 49, 36, 14, 41, 31, 8,  26, 3,  45},
+                             {4, 5}, dtype::Int32());
+  Tensor truth1 = F::from_list({31, 8,  26, 3,  45, 16, 49, 36, 14, 41,
+                                4,  17, 40, 29, 16, 48, 43, 48, 0,  49},
+                               {4, 5}, dtype::Int32());
+  Param p1({true, false, false, false});
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->flip(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list({25, 36, 9,  40, 42, 42, 12, 32, 8, 29, 37, 14,
+                              13, 4,  44, 20, 19, 7,  12, 35, 5, 23, 25, 25},
+                             {2, 3, 4}, dtype::Int32());
+  Tensor truth2 = F::from_list({20, 44, 4,  13, 35, 12, 7,  19, 25, 25, 23, 5,
+                                40, 9,  36, 25, 32, 12, 42, 42, 14, 37, 29, 8},
+                               {2, 3, 4}, dtype::Int32());
+  Param p2({true, false, true, false});
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->flip(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_int32>(pred2, truth2, 0.0001f);
+
+  // Group 2
+  Tensor inp3 = F::from_list(
+      {49, 8,  2,  7,  16, 40, 22, 5,  22, 4,  19, 20, 20, 44, 21, 47, 28, 21,
+       18, 41, 38, 1,  34, 10, 9,  4,  7,  4,  11, 17, 45, 6,  0,  26, 22, 45,
+       40, 18, 26, 39, 32, 27, 23, 31, 15, 37, 11, 26, 10, 27, 3,  37, 47, 41,
+       23, 36, 45, 10, 31, 24, 33, 10, 8,  4,  34, 8,  32, 45, 25, 31, 28, 45,
+       0,  39, 5,  38, 33, 19, 13, 3,  41, 3,  34, 7,  42, 28, 5,  7,  49, 39,
+       13, 1,  32, 20, 28, 2,  39, 0,  9,  4,  17, 48, 7,  2,  21, 43, 41, 23,
+       30, 20, 44, 13, 47, 49, 32, 30, 22, 40, 39, 2},
+      {3, 4, 2, 5}, dtype::Int32());
+  Tensor truth3 = F::from_list(
+      {32, 49, 47, 13, 44, 2,  39, 40, 22, 30, 21, 2,  7,  48, 17, 20, 30, 23,
+       41, 43, 28, 20, 32, 1,  13, 4,  9,  0,  39, 2,  42, 7,  34, 3,  41, 39,
+       49, 7,  5,  28, 5,  39, 0,  45, 28, 3,  13, 19, 33, 38, 34, 4,  8,  10,
+       33, 31, 25, 45, 32, 8,  23, 41, 47, 37, 3,  24, 31, 10, 45, 36, 15, 31,
+       23, 27, 32, 27, 10, 26, 11, 37, 22, 26, 0,  6,  45, 39, 26, 18, 40, 45,
+       9,  10, 34, 1,  38, 17, 11, 4,  7,  4,  21, 44, 20, 20, 19, 41, 18, 21,
+       28, 47, 16, 7,  2,  8,  49, 4,  22, 5,  22, 40},
+      {3, 4, 2, 5}, dtype::Int32());
+  Param p3({true, true, false, true});
+
+  Tensor pred3;
+  ASSERT_TRUE(oprs->flip(inp3, pred3, p3).is_ok());
+  assert_same_data<nn_int32>(pred3, truth3, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/interelem.cpp b/core/test/naive/interelem.cpp
new file mode 100644
index 0000000..bd08f2c
--- /dev/null
+++ b/core/test/naive/interelem.cpp
@@ -0,0 +1,72 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::interelem;
+
+TEST(Naive, Interelem) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor a1 = F::from_list({1, 2, 3, 4, 5, 6}, {2, 3}, dtype::Int32());
+  Tensor b1 = F::from_list({-1, 1, 2, 1, -1, 3}, {2, 3}, dtype::Int32());
+  Tensor truth1 = F::from_list({-1, 2, 6, 4, -5, 18}, {2, 3}, dtype::Int32());
+  Param p1(Param::Operation::Mul);
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->interelem(a1, b1, pred1, p1).is_ok());
+  assert_same_data<int>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor a2 = F::from_list({1, 3, 5, 7, 9, 0}, {2, 3}, dtype::Int32());
+  Tensor b2 = F::from_list({1, 2, 3}, {3}, dtype::Int32());
+  Tensor truth2 = F::from_list({1, 6, 15, 7, 18, 0}, {2, 3}, dtype::Int32());
+  Param p2(Param::Operation::Mul);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->interelem(a2, b2, pred2, p2).is_ok());
+  assert_same_data<int>(pred2, truth2, 0.0001f);
+
+  // Group 4
+  Tensor a4 = F::from_list({1, 2, 5, -2, -4, 6, 1, 2, 5, -2, -4, 6,
+                            1, 2, 5, -2, -4, 6, 1, 2, 5, -2, -4, 6},
+                           {1, 4, 2, 3}, dtype::Int32());
+  Tensor b4 = F::from_list({1, 1, 2, 3, -2, -4, 8, 15, -7, -1, 5, 0},
+                           {1, 4, 1, 3}, dtype::Int32());
+  Tensor truth4 =
+      F::from_list({1, 2,  10,  -2,  -4,  12,  3,  -4, -20, -6, 8,   -24,
+                    8, 30, -35, -16, -60, -42, -1, 10, 0,   2,  -20, 0},
+                   {1, 4, 2, 3}, dtype::Int32());
+  Param p4(Param::Operation::Mul);
+
+  Tensor pred4;
+  ASSERT_TRUE(oprs->interelem(a4, b4, pred4, p4).is_ok());
+  assert_same_data<int>(pred4, truth4, 0.0001f);
+
+  // Group 5
+  Tensor a5 = F::from_list({1, 2, 35, 43, 5, 14}, {2, 3}, dtype::Int32());
+  Tensor b5 = F::from_list({1, 2, 9, 5, 2, 4}, {2, 3}, dtype::Int32());
+  Tensor truth5 = F::from_list({0, 0, 8, 3, 1, 2}, {2, 3}, dtype::Int32());
+  Param p5(Param::Operation::Mod);
+
+  Tensor pred5;
+  ASSERT_TRUE(oprs->interelem(a5, b5, pred5, p5).is_ok());
+  assert_same_data<int>(pred5, truth5, 0.0001f);
+
+  // Group 5
+  Tensor a6 = F::from_list({1, 2, 4}, {3}, dtype::Int32());
+  Tensor b6 = F::from_list({1, 3, 1}, {3}, dtype::Int32());
+  Tensor truth6 = F::from_list({0, 1, 5}, {3}, dtype::Int32());
+  Param p6(Param::Operation::Xor);
+
+  Tensor pred6;
+  ASSERT_TRUE(oprs->interelem(a6, b6, pred6, p6).is_ok());
+  assert_same_data<int>(pred6, truth6, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/linspace.cpp b/core/test/naive/linspace.cpp
new file mode 100644
index 0000000..1c1059b
--- /dev/null
+++ b/core/test/naive/linspace.cpp
@@ -0,0 +1,45 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::linspace;
+
+TEST(Naive, Linspace) {
+  OpBase *oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor t1({50}, dtype::Float64());
+  Tensor truth1 = F::from_list(
+      {.0,         0.20408163, 0.40816327, 0.6122449,  0.81632653, 1.02040816,
+       1.2244898,  1.42857143, 1.63265306, 1.83673469, 2.04081633, 2.24489796,
+       2.44897959, 2.65306122, 2.85714286, 3.06122449, 3.26530612, 3.46938776,
+       3.67346939, 3.87755102, 4.08163265, 4.28571429, 4.48979592, 4.69387755,
+       4.89795918, 5.10204082, 5.30612245, 5.51020408, 5.71428571, 5.91836735,
+       6.12244898, 6.32653061, 6.53061224, 6.73469388, 6.93877551, 7.14285714,
+       7.34693878, 7.55102041, 7.75510204, 7.95918367, 8.16326531, 8.36734694,
+       8.57142857, 8.7755102,  8.97959184, 9.18367347, 9.3877551,  9.59183673,
+       9.79591837, 10.0},
+      {50}, dtype::Float64());
+  Param p1(0, 10, 50, true);
+
+  ASSERT_TRUE(oprs->linspace(t1, p1).is_ok());
+  assert_same_data<nn_float64>(t1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor t2({20}, dtype::Float64());
+  Tensor truth2 =
+      F::from_list({-3.0, -2.7, -2.4, -2.1, -1.8, -1.5, -1.2, -0.9, -0.6, -0.3,
+                    .0,   0.3,  0.6,  0.9,  1.2,  1.5,  1.8,  2.1,  2.4,  2.7},
+                   {20}, dtype::Float64());
+  Param p2(-3, 3, 20, false);
+
+  ASSERT_TRUE(oprs->linspace(t2, p2).is_ok());
+  assert_same_data<nn_float64>(t2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/matmul.cpp b/core/test/naive/matmul.cpp
index 1e8692e..052ee70 100644
--- a/core/test/naive/matmul.cpp
+++ b/core/test/naive/matmul.cpp
@@ -1,8 +1,4 @@
-#include "core/test/common/matmul.h"
-
-#include "core/base/include/tensor.h"
 #include "core/op/naive/ops.h"
-#include "core/test/common/checker.h"
 #include "core/test/common/factory.h"
 #include "core/test/common/utils.h"
 #include "gtest/gtest.h"
diff --git a/core/test/naive/matrix_inverse.cpp b/core/test/naive/matrix_inverse.cpp
new file mode 100644
index 0000000..1ff37ac
--- /dev/null
+++ b/core/test/naive/matrix_inverse.cpp
@@ -0,0 +1,76 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::matrix_inverse;
+
+TEST(Naive, MatrixInverse) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 =
+      F::from_list({6.2996088981373095, 15.529909411050966, 9.973696009883838,
+                    13.604419859345294, 6.161445203564428, 11.872054322398043,
+                    11.178819369551027, 5.762576199891843, 7.840890251608803},
+                   {3, 3}, dtype::Float64());
+  Tensor truth1 = F::from_list(
+      {-0.05392654626015786, -0.17247493499143246, 0.3297430130719682,
+       0.069867543619792, -0.16658791159810454, 0.16336194664421153,
+       0.025535120303034797, 0.36833083819999024, -0.4626417576063726},
+      {3, 3}, dtype::Float64());
+  Param p1;
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->matrix_inverse(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_float64>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list(
+      {11.224801758562354,   14.41416893509421,  4.078127530814076,
+       11.527661693024209,   19.948184732143392, 0.46077839357636874,
+       14.716730386766848,   16.89504351310734,  14.038969253487672,
+       18.103388548238403,   18.4767215732879,   16.242950035265963,
+       9.926569627690473,    2.1653071830249027, 1.659659966235254,
+       1.9983148345568957,   14.438131441919431, 12.067212597776892,
+       6.4109934485673765,   2.1506976676348244, 7.89880490094526,
+       4.282743906051074,    4.354109170045954,  15.564372519542466,
+       15.064077173959177,   5.490566807511515,  18.983511731385963,
+       6.227823019976782,    9.378840180869146,  13.245736640688573,
+       8.175816558089517,    8.501453185684705,  7.718413714716974,
+       11.25051526015769,    2.465472641360016,  11.605845550447809,
+       0.5039240165978454,   8.578240296130923,  0.1408098242713307,
+       15.120068206066025,   5.102632123037562,  11.853811041048763,
+       0.051726871443027456, 10.798871849199262, 5.319865892458293,
+       13.288271852198505,   3.3851467146239655, 14.109638596530258},
+      {3, 4, 4}, dtype::Float64());
+  Tensor truth2 = F::from_list(
+      {-0.007869224564088591, -0.0010883024391001644, -0.008523749627726713,
+       0.12388013673352963,   0.02408106019518925,    -0.05248807449743977,
+       0.033750699988019085,  0.030515238539400233,   -0.10760679868109962,
+       -0.009250500089678336, 0.08321225384347562,    0.022583974415004166,
+       0.10236735736737716,   0.06996324949349707,    -0.06333991179114414,
+       -0.16677121163604855,  0.12177735136477986,    0.052668831250890134,
+       0.0023235128729524918, -0.12893489834324792,   -0.013417508878936899,
+       -0.04852261376613278,  -0.035150625716002866,  0.11797899669893892,
+       -0.08113665685413295,  -0.048941346154906244,  0.06409627950022707,
+       0.06317290292872138,   -0.03541125240167102,   0.06456315979502113,
+       -0.009437839202481294, 0.015297504704594114,   0.32022260642331757,
+       0.004368936792535851,  -0.046226860643948174,  -0.2327001338591773,
+       -0.2655725226528194,   -0.12903780101602638,   0.21055934847278743,
+       0.19557202639793922,   -0.04605783221338342,   -0.08378540254487664,
+       -0.2729971243530408,   0.336609759123423,      0.14042677715830285,
+       0.13998041749459614,   -0.11537601296661658,   -0.10633540739655928},
+      {3, 4, 4}, dtype::Float64());
+  Param p2;
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->matrix_inverse(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_float64>(pred2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/max.cpp b/core/test/naive/max.cpp
new file mode 100644
index 0000000..474d7b4
--- /dev/null
+++ b/core/test/naive/max.cpp
@@ -0,0 +1,32 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::max;
+
+TEST(Naive, Max) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list(
+      {38, 41, 15, 18, 29, 32, 14, 31, 13, 30, 45, 16, 34, 30, 16, 16, 36, 10,
+       4,  46, 39, 27, 10, 11, 19, 31, 3,  10, 44, 27, 9,  7,  43, 1,  41, 14,
+       31, 41, 12, 37, 4,  45, 9,  3,  2,  31, 28, 17, 38, 30, 20, 47, 42, 7,
+       14, 40, 39, 33, 30, 41, 21, 37, 16, 4,  31, 26, 9,  7,  42, 40, 41, 42},
+      {3, 4, 2, 3}, dtype::Int32());
+  Tensor truth1 = F::from_list(
+      {38, 46, 39, 30, 45, 32, 31, 41, 43, 37, 44, 45, 38, 40, 42, 47, 42, 42},
+      {3, 1, 2, 3}, dtype::Int32());
+  Param p1({false, true, false, false});
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->max(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/mean.cpp b/core/test/naive/mean.cpp
new file mode 100644
index 0000000..53f3904
--- /dev/null
+++ b/core/test/naive/mean.cpp
@@ -0,0 +1,45 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::mean;
+
+TEST(Naive, Mean) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list(
+      {31, 5,  34, 48, 1,  41, 29, 44, 21, 39, 5,  40, 29, 24, 24, 6,
+       13, 43, 10, 12, 33, 19, 46, 22, 24, 45, 15, 12, 7,  3,  2,  12,
+       14, 19, 23, 48, 0,  10, 42, 15, 10, 8,  11, 18, 23, 11, 37, 9},
+      {2, 4, 3, 2}, dtype::Int32());
+  Tensor truth1 =
+      F::from_list({23.0, 28.583333333333332, 17.333333333333332, 17.5},
+                   {2, 1, 1, 2}, dtype::Float64());
+  Param p1({false, true, true, false});
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->mean(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_float64>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list(
+      {25, 37, 43, 28, 10, 27, 4,  25, 26, 14, 36, 18, 41, 47, 22, 27,
+       16, 38, 28, 29, 43, 4,  30, 36, 40, 6,  35, 1,  35, 1,  47, 48,
+       7,  2,  35, 28, 26, 37, 49, 46, 7,  37, 30, 38, 12, 32, 9,  21},
+      {2, 4, 3, 2}, dtype::Int32());
+  Tensor truth2 = F::from_list({24.0, 24.166666666666668, 32.75, 26.0},
+                               {1, 4, 1, 1}, dtype::Float64());
+  Param p2({true, false, true, true});
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->mean(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_float64>(pred2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/min.cpp b/core/test/naive/min.cpp
new file mode 100644
index 0000000..34b2561
--- /dev/null
+++ b/core/test/naive/min.cpp
@@ -0,0 +1,32 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::min;
+
+TEST(Naive, Min) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list(
+      {26,  4,   43,  -6,  -10, 9,   6,   1,   -7,  29,  27,  -47, -6,  -6, -48,
+       -39, -35, 41,  38,  -5,  43,  14,  40,  -49, 48,  -29, -7,  -27, 17, -41,
+       42,  45,  45,  49,  -14, 20,  -38, -32, 2,   20,  -4,  37,  -3,  22, -10,
+       -47, 2,   -7,  -42, -38, -36, -42, -16, 8,   33,  19,  -15, 4,   34, -41,
+       -30, 29,  -44, 46,  45,  40,  3,   3,   -44, -25, -14, -15},
+      {3, 4, 2, 3}, dtype::Int32());
+  Tensor truth1 = F::from_list({-42, -42, -15, -47, -48, -39, -44, -49},
+                               {1, 4, 2, 1}, dtype::Int32());
+  Param p1({true, false, false, true});
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->min(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/onehot.cpp b/core/test/naive/onehot.cpp
new file mode 100644
index 0000000..a641db4
--- /dev/null
+++ b/core/test/naive/onehot.cpp
@@ -0,0 +1,27 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::onehot;
+
+TEST(Naive, Onehot) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list({2, 3, 4, 0}, {4}, dtype::Int32());
+  Tensor truth1 =
+      F::from_list({0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0},
+                   {4, 5}, dtype::Int32());
+  Param p1(4);
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->onehot(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/pad.cpp b/core/test/naive/pad.cpp
new file mode 100644
index 0000000..255eb47
--- /dev/null
+++ b/core/test/naive/pad.cpp
@@ -0,0 +1,68 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::pad;
+
+TEST(Naive, Pad) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list(
+      {16, 30, 14, 28, 33, 22, 10, 11, 25, 34, 38, 48, 22, 20, 32, 34, 47, 16},
+      {2, 3, 3}, dtype::Int32());
+  Tensor truth1 = F::from_list(
+      {5,  5,  3,  3, 3,  6,  5,  5,  1,  1, 1, 6,  5,  5,  1,  1, 1, 6,  5,
+       5,  1,  1,  1, 6,  5,  5,  4,  4,  4, 6, 5,  5,  3,  3,  3, 6, 5,  5,
+       16, 30, 14, 6, 5,  5,  28, 33, 22, 6, 5, 5,  10, 11, 25, 6, 5, 5,  4,
+       4,  4,  6,  5, 5,  3,  3,  3,  6,  5, 5, 34, 38, 48, 6,  5, 5, 22, 20,
+       32, 6,  5,  5, 34, 47, 16, 6,  5,  5, 4, 4,  4,  6,  5,  5, 3, 3,  3,
+       6,  5,  5,  2, 2,  2,  6,  5,  5,  2, 2, 2,  6,  5,  5,  2, 2, 2,  6,
+       5,  5,  4,  4, 4,  6,  5,  5,  3,  3, 3, 6,  5,  5,  2,  2, 2, 6,  5,
+       5,  2,  2,  2, 6,  5,  5,  2,  2,  2, 6, 5,  5,  4,  4,  4, 6},
+      {5, 5, 6}, dtype::Int32());
+  Param p1(Param::Mode::Constant, 6, {1, 2, 1, 1, 2, 1}, {1, 2, 3, 4, 5, 6});
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->pad(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list({10, 17, 44, 19, 18, 29, 39, 13, 14, 13, 37, 35},
+                             {2, 3, 2}, dtype::Int32());
+  Tensor truth2 = F::from_list(
+      {10, 10, 10, 17, 17, 17, 17, 10, 10, 10, 17, 17, 17, 17, 44, 44, 44, 19,
+       19, 19, 19, 18, 18, 18, 29, 29, 29, 29, 18, 18, 18, 29, 29, 29, 29, 10,
+       10, 10, 17, 17, 17, 17, 10, 10, 10, 17, 17, 17, 17, 44, 44, 44, 19, 19,
+       19, 19, 18, 18, 18, 29, 29, 29, 29, 18, 18, 18, 29, 29, 29, 29, 39, 39,
+       39, 13, 13, 13, 13, 39, 39, 39, 13, 13, 13, 13, 14, 14, 14, 13, 13, 13,
+       13, 37, 37, 37, 35, 35, 35, 35, 37, 37, 37, 35, 35, 35, 35, 39, 39, 39,
+       13, 13, 13, 13, 39, 39, 39, 13, 13, 13, 13, 14, 14, 14, 13, 13, 13, 13,
+       37, 37, 37, 35, 35, 35, 35, 37, 37, 37, 35, 35, 35, 35, 39, 39, 39, 13,
+       13, 13, 13, 39, 39, 39, 13, 13, 13, 13, 14, 14, 14, 13, 13, 13, 13, 37,
+       37, 37, 35, 35, 35, 35, 37, 37, 37, 35, 35, 35, 35},
+      {5, 5, 7}, dtype::Int32());
+  Param p2(Param::Mode::Edge, 6, {1, 2, 1, 1, 2, 3}, {});
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->pad(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_int32>(pred2, truth2, 0.0001f);
+
+  // Group 3
+  Tensor inp3 = F::from_list({27, 42, 27, 41, 26, 28}, {2, 3}, dtype::Int32());
+  Tensor truth3 = F::from_list({42, 41, 42, 28, 42, 42, 27, 42, 27, 42,
+                                41, 41, 26, 28, 41, 41, 41, 42, 28, 41},
+                               {4, 5}, dtype::Int32());
+  Param p3(Param::Mode::Maximum, 4, {1, 1, 1, 1}, {});
+
+  Tensor pred3;
+  ASSERT_TRUE(oprs->pad(inp3, pred3, p3).is_ok());
+  assert_same_data<nn_int32>(pred3, truth3, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/permute.cpp b/core/test/naive/permute.cpp
index 459a38f..184c91b 100644
--- a/core/test/naive/permute.cpp
+++ b/core/test/naive/permute.cpp
@@ -1,6 +1,4 @@
-#include "core/base/include/tensor.h"
 #include "core/op/naive/ops.h"
-#include "core/test/common/checker.h"
 #include "core/test/common/factory.h"
 #include "core/test/common/utils.h"
 #include "gtest/gtest.h"
diff --git a/core/test/naive/repeat.cpp b/core/test/naive/repeat.cpp
new file mode 100644
index 0000000..c9fcf31
--- /dev/null
+++ b/core/test/naive/repeat.cpp
@@ -0,0 +1,50 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::repeat;
+
+TEST(Naive, Repeat) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list({8, 35, 28, 42, 8, 26, 43, 43, 0, 14, 9, 32},
+                             {3, 4}, dtype::Int32());
+  Tensor truth1 = F::from_list({8,  8,  35, 35, 28, 28, 42, 42, 8, 8, 26, 26,
+                                43, 43, 43, 43, 0,  0,  14, 14, 9, 9, 32, 32},
+                               {3, 8}, dtype::Int32());
+  Param p1(2, 1);
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->repeat(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list(
+      {31, 4, 2,  29, 20, 14, 45, 33, 16, 48, 49, 18, 33, 4,  4,  7,
+       1,  8, 23, 41, 49, 16, 22, 1,  0,  11, 23, 20, 36, 35, 21, 14,
+       42, 1, 1,  25, 27, 17, 8,  45, 25, 23, 11, 27, 4,  17, 29, 14},
+      {2, 3, 4, 2}, dtype::Int32());
+  Tensor truth2 = F::from_list(
+      {31, 4,  2,  29, 20, 14, 45, 33, 31, 4,  2,  29, 20, 14, 45, 33, 31, 4,
+       2,  29, 20, 14, 45, 33, 16, 48, 49, 18, 33, 4,  4,  7,  16, 48, 49, 18,
+       33, 4,  4,  7,  16, 48, 49, 18, 33, 4,  4,  7,  1,  8,  23, 41, 49, 16,
+       22, 1,  1,  8,  23, 41, 49, 16, 22, 1,  1,  8,  23, 41, 49, 16, 22, 1,
+       0,  11, 23, 20, 36, 35, 21, 14, 0,  11, 23, 20, 36, 35, 21, 14, 0,  11,
+       23, 20, 36, 35, 21, 14, 42, 1,  1,  25, 27, 17, 8,  45, 42, 1,  1,  25,
+       27, 17, 8,  45, 42, 1,  1,  25, 27, 17, 8,  45, 25, 23, 11, 27, 4,  17,
+       29, 14, 25, 23, 11, 27, 4,  17, 29, 14, 25, 23, 11, 27, 4,  17, 29, 14},
+      {2, 9, 4, 2}, dtype::Int32());
+  Param p2(3, 1);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->repeat(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_int32>(pred2, truth2, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/rotate.cpp b/core/test/naive/rotate.cpp
new file mode 100644
index 0000000..0c6bec6
--- /dev/null
+++ b/core/test/naive/rotate.cpp
@@ -0,0 +1,67 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::rotate;
+
+TEST(Naive, Rotate) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list({36, 47, 38, 32, 0,  21, 37, 0, 6,  37,
+                              5,  38, 33, 0,  15, 20, 12, 6, 10, 40},
+                             {4, 5}, dtype::Int32());
+  Tensor truth1 = F::from_list({20, 5,  21, 36, 12, 38, 37, 47, 6,  33,
+                                0,  38, 10, 0,  6,  32, 40, 15, 37, 0},
+                               {5, 4}, dtype::Int32());
+  Param p1(1, 0, 1);
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->rotate(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list({39, 13, 44, 29, 1, 12, 5, 34, 33, 12, 22, 42,
+                              34, 3,  10, 17, 0, 41, 4, 8,  48, 36, 21, 47},
+                             {3, 2, 4}, dtype::Int32());
+  Tensor truth2 = F::from_list({8,  4,  41, 0,  47, 21, 36, 48, 42, 22, 12, 33,
+                                17, 10, 3,  34, 29, 44, 13, 39, 34, 5,  12, 1},
+                               {3, 2, 4}, dtype::Int32());
+  Param p2(2, 0, 2);
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->rotate(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_int32>(pred2, truth2, 0.0001f);
+
+  // Group 3
+  Tensor inp3 = F::from_list(
+      {35, 22, 31, 18, 20, 19, 11, 28, 10, 35, 6,  14, 1,  15, 48, 38, 41, 38,
+       17, 29, 31, 35, 49, 47, 49, 4,  17, 41, 18, 48, 3,  27, 16, 35, 33, 28,
+       30, 1,  9,  43, 16, 34, 18, 23, 35, 49, 40, 18, 19, 4,  23, 8,  44, 19,
+       18, 25, 14, 21, 37, 42, 28, 16, 29, 44, 16, 3,  37, 14, 49, 46, 32, 33,
+       13, 12, 28, 21, 22, 11, 7,  42, 33, 34, 10, 41, 0,  12, 43, 5,  18, 35,
+       47, 0,  1,  30, 45, 4,  49, 12, 9,  2,  13, 14, 42, 20, 8,  28, 46, 31,
+       10, 34, 19, 34, 42, 39, 10, 22, 36, 42, 20, 49},
+      {3, 2, 4, 5}, dtype::Int32());
+  Tensor truth3 = F::from_list(
+      {38, 41, 38, 17, 29, 28, 30, 1,  9,  43, 6,  14, 1,  15, 48, 3,  27, 16,
+       35, 33, 19, 11, 28, 10, 35, 4,  17, 41, 18, 48, 35, 22, 31, 18, 20, 31,
+       35, 49, 47, 49, 25, 14, 21, 37, 42, 21, 22, 11, 7,  42, 23, 8,  44, 19,
+       18, 32, 33, 13, 12, 28, 49, 40, 18, 19, 4,  3,  37, 14, 49, 46, 16, 34,
+       18, 23, 35, 28, 16, 29, 44, 16, 4,  49, 12, 9,  2,  22, 36, 42, 20, 49,
+       47, 0,  1,  30, 45, 19, 34, 42, 39, 10, 12, 43, 5,  18, 35, 28, 46, 31,
+       10, 34, 33, 34, 10, 41, 0,  13, 14, 42, 20, 8},
+      {3, 4, 2, 5}, dtype::Int32());
+  Param p3(3, 1, 2);
+
+  Tensor pred3;
+  ASSERT_TRUE(oprs->rotate(inp3, pred3, p3).is_ok());
+  assert_same_data<nn_int32>(pred3, truth3, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/sort.cpp b/core/test/naive/sort.cpp
new file mode 100644
index 0000000..348a1bd
--- /dev/null
+++ b/core/test/naive/sort.cpp
@@ -0,0 +1,61 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::sort;
+
+TEST(Naive, Sort) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list(
+      {31, 37, 18, 16, 6,  38, 43, 28, 36, 12, 16, 28, 10, 29, 5,  5,  42, 29,
+       3,  40, 9,  22, 18, 29, 21, 3,  25, 15, 19, 39, 49, 34, 8,  34, 22, 9,
+       27, 12, 4,  27, 40, 17, 10, 24, 6,  42, 21, 39, 18, 40, 40, 16, 10, 23,
+       14, 25, 43, 48, 0,  17, 9,  0,  41, 0,  33, 47, 35, 42, 23, 38, 7,  22,
+       11, 0,  19, 9,  7,  45, 23, 29, 2,  0,  38, 6,  41, 15, 8,  46, 34, 24,
+       14, 28, 49, 19, 12, 47, 24, 5,  23, 12, 39, 40, 44, 46, 39, 34, 24, 5,
+       28, 21, 36, 7,  14, 43, 11, 8,  38, 6,  7,  6,  12, 4,  9,  10, 42, 7,
+       33, 9,  48, 36, 47, 25, 34, 35, 22, 7,  1,  1,  38, 8,  7,  44, 37, 19,
+       28, 36, 8,  30, 49, 9,  47, 10, 39, 14, 27, 49, 6,  27, 28, 48, 8,  38,
+       18, 16, 5,  4,  6,  48, 2,  11, 41, 24, 17, 37, 49, 45, 28, 41, 0,  4,
+       2,  6,  45, 18, 10, 47, 31, 14, 45, 0,  13, 40, 24, 42, 25, 48, 3,  33,
+       33, 17, 22, 31, 37, 3,  25, 5,  28, 37, 12, 20, 44, 8,  45, 36, 34, 42,
+       22, 31, 26, 42, 11, 47, 45, 22, 39, 32, 28, 22, 26, 17, 28, 42, 3,  27,
+       33, 4,  34, 6,  12, 23},
+      {4, 3, 4, 5}, dtype::Int32());
+  Tensor truth1 = F::from_list(
+      {2,  0,  1,  0,  0,  3,  1,  5,  0,  5,  7,  8,  6,  3,  6,  7,  8,  10,
+       14, 8,  2,  0,  3,  0,  4,  4,  6,  8,  2,  5,  8,  17, 9,  12, 11, 14,
+       28, 10, 23, 11, 4,  6,  5,  3,  9,  5,  7,  9,  4,  12, 9,  22, 15, 16,
+       12, 28, 31, 18, 23, 21, 5,  3,  6,  0,  6,  7,  4,  10, 18, 6,  13, 22,
+       18, 19, 14, 36, 22, 22, 23, 17, 7,  8,  11, 6,  10, 9,  10, 14, 19, 12,
+       32, 25, 18, 22, 17, 38, 33, 29, 27, 19, 8,  16, 12, 10, 20, 11, 21, 30,
+       23, 22, 42, 34, 37, 29, 25, 44, 38, 39, 36, 27, 9,  6,  14, 7,  17, 12,
+       7,  28, 24, 21, 16, 28, 33, 26, 28, 40, 31, 41, 34, 36, 9,  24, 24, 16,
+       22, 15, 24, 37, 26, 27, 39, 28, 37, 34, 33, 47, 35, 44, 37, 39, 25, 24,
+       27, 19, 24, 34, 25, 39, 33, 29, 47, 38, 41, 35, 33, 47, 40, 45, 45, 42,
+       22, 27, 17, 12, 23, 36, 28, 38, 28, 38, 42, 34, 45, 36, 40, 45, 43, 45,
+       46, 42, 31, 28, 31, 18, 25, 41, 40, 42, 29, 39, 47, 42, 46, 42, 40, 48,
+       44, 48, 48, 48, 40, 37, 34, 28, 34, 47, 42, 45, 38, 39, 49, 43, 48, 43,
+       41, 49, 47, 49, 49, 49},
+      {4, 3, 4, 5}, dtype::Int32());
+  Param p1(0, Param::Order::Increase);
+  Param p2(2, Param::Order::Increase);
+  Param p3(1, Param::Order::Increase);
+
+  Tensor pred1;
+  Tensor pred2;
+  Tensor pred3;
+  ASSERT_TRUE(oprs->sort(inp1, pred1, p1).is_ok());
+  ASSERT_TRUE(oprs->sort(pred1, pred2, p2).is_ok());
+  ASSERT_TRUE(oprs->sort(pred2, pred3, p3).is_ok());
+  assert_same_data<nn_int32>(pred3, truth1, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/sum.cpp b/core/test/naive/sum.cpp
new file mode 100644
index 0000000..40afc0c
--- /dev/null
+++ b/core/test/naive/sum.cpp
@@ -0,0 +1,60 @@
+#include "core/op/naive/ops.h"
+#include "core/test/common/factory.h"
+#include "core/test/common/utils.h"
+#include "gtest/gtest.h"
+
+using namespace nncore;
+using namespace test;
+using namespace opr;
+using namespace opr::naive;
+
+using F = NDArrayFactory;
+using Param = param::sum;
+
+TEST(Naive, Sum) {
+  OpBase* oprs = OpNaiveImpl::get_instance();
+
+  // Group 1
+  Tensor inp1 = F::from_list(
+      {19, 25, 12, 16, 22, 1,  37, 3,  22, 2,  27, 9,  21, 29, 13, 13, 10, 14,
+       33, 44, 15, 6,  48, 25, 15, 37, 6,  30, 6,  14, 26, 0,  30, 4,  17, 7,
+       26, 28, 28, 27, 42, 39, 38, 47, 25, 31, 34, 25, 11, 22, 26, 6,  47, 35,
+       13, 46, 40, 21, 39, 18, 10, 10, 40, 40, 13, 31, 24, 11, 19, 31, 5,  16},
+      {3, 4, 2, 3}, dtype::Int32());
+  Tensor truth1 = F::from_list({273, 193, 306, 276, 272, 302}, {3, 1, 2, 1},
+                               dtype::Int32());
+  Param p1({false, true, false, true});
+
+  Tensor pred1;
+  ASSERT_TRUE(oprs->sum(inp1, pred1, p1).is_ok());
+  assert_same_data<nn_int32>(pred1, truth1, 0.0001f);
+
+  // Group 2
+  Tensor inp2 = F::from_list(
+      {36, 33, 3,  25, 9,  19, 18, 45, 21, 41, 32, 19, 23, 10, 3,  17, 26, 33,
+       37, 25, 44, 6,  7,  27, 46, 29, 42, 19, 42, 42, 0,  43, 4,  49, 27, 22,
+       10, 2,  5,  21, 47, 39, 21, 32, 5,  1,  43, 40, 9,  30, 10, 29, 36, 46,
+       47, 27, 36, 39, 26, 28, 5,  49, 21, 45, 45, 7,  44, 16, 44, 44, 1,  45},
+      {3, 4, 2, 3}, dtype::Int32());
+  Tensor truth2 = F::from_list({238, 267, 241, 283, 128, 280, 268, 214},
+                               {1, 4, 2, 1}, dtype::Int32());
+  Param p2({true, false, false, true});
+
+  Tensor pred2;
+  ASSERT_TRUE(oprs->sum(inp2, pred2, p2).is_ok());
+  assert_same_data<nn_int32>(pred2, truth2, 0.0001f);
+
+  // Group 3
+  Tensor inp3 = F::from_list(
+      {18, 4,  35, 22, 34, 18, 15, 5,  13, 49, 41, 3,  4,  35, 14, 22, 38, 29,
+       42, 47, 30, 4,  9,  0,  35, 0,  43, 27, 44, 29, 2,  17, 16, 6,  36, 41,
+       1,  22, 3,  30, 35, 13, 41, 45, 38, 44, 38, 21, 42, 41, 19, 24, 40, 39,
+       37, 24, 28, 17, 22, 21, 15, 23, 35, 16, 31, 3,  29, 28, 0,  15, 17, 10},
+      {3, 4, 2, 3}, dtype::Int32());
+  Tensor truth3 = F::from_list({1734}, {1, 1, 1, 1}, dtype::Int32());
+  Param p3({true, true, true, true});
+
+  Tensor pred3;
+  ASSERT_TRUE(oprs->sum(inp3, pred3, p3).is_ok());
+  assert_same_data<nn_int32>(pred3, truth3, 0.0001f);
+}
\ No newline at end of file
diff --git a/core/test/naive/transpose.cpp b/core/test/naive/transpose.cpp
index 9e6d1eb..2a19cb6 100644
--- a/core/test/naive/transpose.cpp
+++ b/core/test/naive/transpose.cpp
@@ -1,6 +1,4 @@
-#include "core/base/include/tensor.h"
 #include "core/op/naive/ops.h"
-#include "core/test/common/checker.h"
 #include "core/test/common/factory.h"
 #include "core/test/common/utils.h"
 #include "gtest/gtest.h"
diff --git a/csharp/CodeGenerator/BasicArithGenerator.cs b/csharp/CodeGenerator/BasicArithGenerator.cs
new file mode 100644
index 0000000..b6d5911
--- /dev/null
+++ b/csharp/CodeGenerator/BasicArithGenerator.cs
@@ -0,0 +1,33 @@
+using System.Text;
+using System.Collections.Generic;
+
+namespace CodeGenerator{
+    public class BasicArithGenerator{
+        public string Name{ get; set; }
+        public string Expr{ get; set; }
+        private List<string> _types= new List<string>(new string[]{ "double", "float", "long", "int", "bool" });
+        private string[] _priority = new string[]{ "double", "float", "long", "int", "bool" };
+        public string GetCode(){
+            StringBuilder builder = new StringBuilder();
+            foreach(var typeA in _types){
+                foreach(var typeB in _types){
+                    string typeRes = null;
+                    for (int i = 0; i < _priority.Length; i++){
+                        if(typeA == _priority[i] || typeB == _priority[i]){
+                            typeRes = _priority[i];
+                            break;
+                        }
+                    }
+                    if(typeRes is null){
+                        throw new System.Exception();
+                    }
+                    builder.Append(@"
+public static Tensor<" + typeRes + $"> {Name}(this Tensor<" + typeA + @"> a, Tensor<" + typeB + @"> b){
+    return InterElemOperation.Execute<" + $"{typeA}, {typeB}, {typeRes}" + $">(a, b, (x, y) => {Expr});" + @"
+}");
+                }
+            }
+            return builder.ToString();
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/BasicFunctionGenerator.cs b/csharp/CodeGenerator/BasicFunctionGenerator.cs
new file mode 100644
index 0000000..ea8e01d
--- /dev/null
+++ b/csharp/CodeGenerator/BasicFunctionGenerator.cs
@@ -0,0 +1,24 @@
+using System.Text;
+using System.Collections.Generic;
+
+namespace CodeGenerator{
+    public class BasicFunctionGenerator{
+        public string Name{ get; set; }
+        public string Expr{ get; set; }
+        public List<string> Parameters{ get; set; }
+        private List<string> _types= new List<string>(new string[]{ "double", "float", "long", "int", "bool" });
+        private string _targetType = null;
+        public string GetCode(){
+            StringBuilder builder = new StringBuilder();
+            foreach(var typeInp in _types){
+                string typeRes = _targetType??typeInp;
+                builder.Append(@"
+public static Tensor<" + typeRes + $"> {Name}(this Tensor<" + typeInp + @"> inp" + (Parameters is null ? "": ", ") + 
+                                    (Parameters is null ? "": string.Join(',', Parameters)) + @"){
+    return OnElemOperation.Execute<" + $"{typeInp}, {typeRes}" + $">(inp, x => {Expr});" + @"
+}");
+            }
+            return builder.ToString();
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Num.NET.csproj b/csharp/CodeGenerator/CodeGenerator.csproj
similarity index 80%
rename from csharp/Num.NET/Num.NET.csproj
rename to csharp/CodeGenerator/CodeGenerator.csproj
index 15f5d98..40c60dd 100644
--- a/csharp/Num.NET/Num.NET.csproj
+++ b/csharp/CodeGenerator/CodeGenerator.csproj
@@ -5,7 +5,6 @@
     <TargetFramework>net6.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
 
 </Project>
diff --git a/csharp/CodeGenerator/Program.cs b/csharp/CodeGenerator/Program.cs
new file mode 100644
index 0000000..d4a670d
--- /dev/null
+++ b/csharp/CodeGenerator/Program.cs
@@ -0,0 +1,73 @@
+﻿using CodeGenerator;
+using System.Collections.Generic;
+
+var AddGenerator = new BasicArithGenerator() { Name = "Add", Expr = "x + y" };
+CodeWriter.Write("Add", AddGenerator.GetCode());
+
+var SubGenerator = new BasicArithGenerator() { Name = "Sub", Expr = "x - y" };
+CodeWriter.Write("Sub", SubGenerator.GetCode());
+
+var DivGenerator = new BasicArithGenerator() { Name = "Div", Expr = "x / y" };
+CodeWriter.Write("Div", DivGenerator.GetCode());
+
+var ModGenerator = new BasicArithGenerator() { Name = "Mod", Expr = "x % y" };
+CodeWriter.Write("Mod", ModGenerator.GetCode());
+
+var AbsGenerator = new BasicFunctionGenerator() { Name = "Abs", Expr = "System.Math.Abs(x)" };
+CodeWriter.Write("Abs", AbsGenerator.GetCode());
+
+var FloorGenerator = new BasicFunctionGenerator() { Name = "Floor", Expr = "System.Math.Floor(x)" };
+CodeWriter.Write("Floor", FloorGenerator.GetCode());
+
+var CeilGenerator = new BasicFunctionGenerator() { Name = "Ceil", Expr = "System.Math.Ceil(x)" };
+CodeWriter.Write("Ceil", CeilGenerator.GetCode());
+
+var RoundGenerator = new BasicFunctionGenerator() { Name = "Round", Expr = "System.Math.Round(x)" };
+CodeWriter.Write("Round", RoundGenerator.GetCode());
+
+var PowGenerator1 = new StaticUtilGenerator() { 
+    Name = "Pow", 
+    Parameters = new List<string>(new string[]{"int y"}), 
+    Expr = "System.Math.Pow(x, y)" };
+var PowGenerator2 = new ReversedStaticUtilGenerator() { 
+    Name = "Pow", 
+    Parameters = new List<string>(new string[]{"int y"}), 
+    Expr = "System.Math.Pow(y, x)" };
+CodeWriter.Write("Pow", PowGenerator1.GetCode() + PowGenerator2.GetCode());
+
+var ClampGenerator = new StaticUtilGenerator(){
+    Name = "Clamp", 
+    Parameters = new List<string>(new string[]{"T min", "T max"}), 
+    Expr = "System.Math.Clamp(x, min, max)" };
+CodeWriter.Write("Clamp", ClampGenerator.GetCode());
+
+var ExpGenerator = new StaticUtilGenerator() { 
+    Name = "Exp", 
+    Expr = "System.Math.Exp(x)" };
+CodeWriter.Write("Exp", ExpGenerator.GetCode());
+
+var LogGenerator1 = new StaticUtilGenerator() { 
+    Name = "Log", 
+    Parameters = new List<string>(new string[]{"double y"}), 
+    Expr = "System.Math.Log(y, x)" };
+var LogGenerator2 = new ReversedStaticUtilGenerator() { 
+    Name = "Log", 
+    Parameters = new List<string>(new string[]{"double baseValue"}), 
+    Expr = "System.Math.Log(x, baseValue)" };
+CodeWriter.Write("Log", LogGenerator1.GetCode() + LogGenerator2.GetCode());
+
+var Log2Generator = new StaticUtilGenerator() { 
+    Name = "Log2", 
+    Expr = "System.Math.Log2(x)" };
+CodeWriter.Write("Log2", Log2Generator.GetCode());
+
+var Log10Generator = new StaticUtilGenerator() { 
+    Name = "Log10", 
+    Expr = "System.Math.Log10(x)" };
+CodeWriter.Write("Log10", Log10Generator.GetCode());
+
+var MaxGenerator = new BasicArithGenerator() { Name = "Max", Expr = "x > y ? x : y" };
+CodeWriter.Write("Max", MaxGenerator.GetCode());
+
+var MinGenerator = new BasicArithGenerator() { Name = "Min", Expr = "x > y ? y : x" };
+CodeWriter.Write("Min", MinGenerator.GetCode());
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Abs.csg b/csharp/CodeGenerator/Results/Abs.csg
new file mode 100644
index 0000000..8d1f79b
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Abs.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Abs(this Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Abs(x));
+}
+public static Tensor<float> Abs(this Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Abs(x));
+}
+public static Tensor<long> Abs(this Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Abs(x));
+}
+public static Tensor<int> Abs(this Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Abs(x));
+}
+public static Tensor<bool> Abs(this Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Abs(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Add.csg b/csharp/CodeGenerator/Results/Add.csg
new file mode 100644
index 0000000..b2348c8
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Add.csg
@@ -0,0 +1,76 @@
+
+public static Tensor<double> Add(this Tensor<double> a, Tensor<double> b){
+    return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<double> a, Tensor<float> b){
+    return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<double> a, Tensor<long> b){
+    return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<double> a, Tensor<int> b){
+    return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<double> a, Tensor<bool> b){
+    return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<float> a, Tensor<double> b){
+    return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<float> a, Tensor<float> b){
+    return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<float> a, Tensor<long> b){
+    return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<float> a, Tensor<int> b){
+    return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<float> a, Tensor<bool> b){
+    return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<long> a, Tensor<double> b){
+    return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<long> a, Tensor<float> b){
+    return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<long> Add(this Tensor<long> a, Tensor<long> b){
+    return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x + y);
+}
+public static Tensor<long> Add(this Tensor<long> a, Tensor<int> b){
+    return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x + y);
+}
+public static Tensor<long> Add(this Tensor<long> a, Tensor<bool> b){
+    return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<int> a, Tensor<double> b){
+    return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<int> a, Tensor<float> b){
+    return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<long> Add(this Tensor<int> a, Tensor<long> b){
+    return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x + y);
+}
+public static Tensor<int> Add(this Tensor<int> a, Tensor<int> b){
+    return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x + y);
+}
+public static Tensor<int> Add(this Tensor<int> a, Tensor<bool> b){
+    return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x + y);
+}
+public static Tensor<double> Add(this Tensor<bool> a, Tensor<double> b){
+    return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x + y);
+}
+public static Tensor<float> Add(this Tensor<bool> a, Tensor<float> b){
+    return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x + y);
+}
+public static Tensor<long> Add(this Tensor<bool> a, Tensor<long> b){
+    return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => x + y);
+}
+public static Tensor<int> Add(this Tensor<bool> a, Tensor<int> b){
+    return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => x + y);
+}
+public static Tensor<bool> Add(this Tensor<bool> a, Tensor<bool> b){
+    return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x + y);
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Ceil.csg b/csharp/CodeGenerator/Results/Ceil.csg
new file mode 100644
index 0000000..28c00cb
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Ceil.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Ceil(this Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Ceil(x));
+}
+public static Tensor<float> Ceil(this Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Ceil(x));
+}
+public static Tensor<long> Ceil(this Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Ceil(x));
+}
+public static Tensor<int> Ceil(this Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Ceil(x));
+}
+public static Tensor<bool> Ceil(this Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Ceil(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Clamp.csg b/csharp/CodeGenerator/Results/Clamp.csg
new file mode 100644
index 0000000..c9ccbdb
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Clamp.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Clamp(Tensor<double> inp, T min,T max){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Clamp(x, min, max));
+}
+public static Tensor<float> Clamp(Tensor<float> inp, T min,T max){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Clamp(x, min, max));
+}
+public static Tensor<long> Clamp(Tensor<long> inp, T min,T max){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Clamp(x, min, max));
+}
+public static Tensor<int> Clamp(Tensor<int> inp, T min,T max){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Clamp(x, min, max));
+}
+public static Tensor<bool> Clamp(Tensor<bool> inp, T min,T max){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Clamp(x, min, max));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Div.csg b/csharp/CodeGenerator/Results/Div.csg
new file mode 100644
index 0000000..7095288
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Div.csg
@@ -0,0 +1,76 @@
+
+public static Tensor<double> Div(this Tensor<double> a, Tensor<double> b){
+    return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<double> a, Tensor<float> b){
+    return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<double> a, Tensor<long> b){
+    return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<double> a, Tensor<int> b){
+    return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<double> a, Tensor<bool> b){
+    return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<float> a, Tensor<double> b){
+    return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<float> a, Tensor<float> b){
+    return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<float> a, Tensor<long> b){
+    return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<float> a, Tensor<int> b){
+    return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<float> a, Tensor<bool> b){
+    return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<long> a, Tensor<double> b){
+    return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<long> a, Tensor<float> b){
+    return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<long> Div(this Tensor<long> a, Tensor<long> b){
+    return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x / y);
+}
+public static Tensor<long> Div(this Tensor<long> a, Tensor<int> b){
+    return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x / y);
+}
+public static Tensor<long> Div(this Tensor<long> a, Tensor<bool> b){
+    return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<int> a, Tensor<double> b){
+    return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<int> a, Tensor<float> b){
+    return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<long> Div(this Tensor<int> a, Tensor<long> b){
+    return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x / y);
+}
+public static Tensor<int> Div(this Tensor<int> a, Tensor<int> b){
+    return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x / y);
+}
+public static Tensor<int> Div(this Tensor<int> a, Tensor<bool> b){
+    return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x / y);
+}
+public static Tensor<double> Div(this Tensor<bool> a, Tensor<double> b){
+    return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x / y);
+}
+public static Tensor<float> Div(this Tensor<bool> a, Tensor<float> b){
+    return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x / y);
+}
+public static Tensor<long> Div(this Tensor<bool> a, Tensor<long> b){
+    return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => x / y);
+}
+public static Tensor<int> Div(this Tensor<bool> a, Tensor<int> b){
+    return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => x / y);
+}
+public static Tensor<bool> Div(this Tensor<bool> a, Tensor<bool> b){
+    return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x / y);
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Exp.csg b/csharp/CodeGenerator/Results/Exp.csg
new file mode 100644
index 0000000..61c3e10
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Exp.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Exp(Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Exp(x));
+}
+public static Tensor<float> Exp(Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Exp(x));
+}
+public static Tensor<long> Exp(Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Exp(x));
+}
+public static Tensor<int> Exp(Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Exp(x));
+}
+public static Tensor<bool> Exp(Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Exp(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Floor.csg b/csharp/CodeGenerator/Results/Floor.csg
new file mode 100644
index 0000000..4ac0d81
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Floor.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Floor(this Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Floor(x));
+}
+public static Tensor<float> Floor(this Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Floor(x));
+}
+public static Tensor<long> Floor(this Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Floor(x));
+}
+public static Tensor<int> Floor(this Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Floor(x));
+}
+public static Tensor<bool> Floor(this Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Floor(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Log.csg b/csharp/CodeGenerator/Results/Log.csg
new file mode 100644
index 0000000..fe4832f
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Log.csg
@@ -0,0 +1,31 @@
+
+public static Tensor<double> Log(Tensor<double> inp, double y){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log(y, x));
+}
+public static Tensor<float> Log(Tensor<float> inp, double y){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Log(y, x));
+}
+public static Tensor<long> Log(Tensor<long> inp, double y){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Log(y, x));
+}
+public static Tensor<int> Log(Tensor<int> inp, double y){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Log(y, x));
+}
+public static Tensor<bool> Log(Tensor<bool> inp, double y){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Log(y, x));
+}
+public static Tensor<double> Log(double baseValue, Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log(x, baseValue));
+}
+public static Tensor<float> Log(double baseValue, Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Log(x, baseValue));
+}
+public static Tensor<long> Log(double baseValue, Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Log(x, baseValue));
+}
+public static Tensor<int> Log(double baseValue, Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Log(x, baseValue));
+}
+public static Tensor<bool> Log(double baseValue, Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Log(x, baseValue));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Log10.csg b/csharp/CodeGenerator/Results/Log10.csg
new file mode 100644
index 0000000..166a59c
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Log10.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Log10(Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log10(x));
+}
+public static Tensor<float> Log10(Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Log10(x));
+}
+public static Tensor<long> Log10(Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Log10(x));
+}
+public static Tensor<int> Log10(Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Log10(x));
+}
+public static Tensor<bool> Log10(Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Log10(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Log2.csg b/csharp/CodeGenerator/Results/Log2.csg
new file mode 100644
index 0000000..be91d33
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Log2.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Log2(Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log2(x));
+}
+public static Tensor<float> Log2(Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Log2(x));
+}
+public static Tensor<long> Log2(Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Log2(x));
+}
+public static Tensor<int> Log2(Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Log2(x));
+}
+public static Tensor<bool> Log2(Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Log2(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Max.csg b/csharp/CodeGenerator/Results/Max.csg
new file mode 100644
index 0000000..2634418
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Max.csg
@@ -0,0 +1,76 @@
+
+public static Tensor<double> Max(this Tensor<double> a, Tensor<double> b){
+    return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<double> a, Tensor<float> b){
+    return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<double> a, Tensor<long> b){
+    return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<double> a, Tensor<int> b){
+    return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<double> a, Tensor<bool> b){
+    return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<float> a, Tensor<double> b){
+    return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<float> a, Tensor<float> b){
+    return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<float> a, Tensor<long> b){
+    return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<float> a, Tensor<int> b){
+    return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<float> a, Tensor<bool> b){
+    return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<long> a, Tensor<double> b){
+    return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<long> a, Tensor<float> b){
+    return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<long> Max(this Tensor<long> a, Tensor<long> b){
+    return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<long> Max(this Tensor<long> a, Tensor<int> b){
+    return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<long> Max(this Tensor<long> a, Tensor<bool> b){
+    return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<int> a, Tensor<double> b){
+    return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<int> a, Tensor<float> b){
+    return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<long> Max(this Tensor<int> a, Tensor<long> b){
+    return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<int> Max(this Tensor<int> a, Tensor<int> b){
+    return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<int> Max(this Tensor<int> a, Tensor<bool> b){
+    return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<double> Max(this Tensor<bool> a, Tensor<double> b){
+    return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<float> Max(this Tensor<bool> a, Tensor<float> b){
+    return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<long> Max(this Tensor<bool> a, Tensor<long> b){
+    return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<int> Max(this Tensor<bool> a, Tensor<int> b){
+    return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => x > y ? x : y);
+}
+public static Tensor<bool> Max(this Tensor<bool> a, Tensor<bool> b){
+    return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x > y ? x : y);
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Min.csg b/csharp/CodeGenerator/Results/Min.csg
new file mode 100644
index 0000000..9817c73
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Min.csg
@@ -0,0 +1,76 @@
+
+public static Tensor<double> Min(this Tensor<double> a, Tensor<double> b){
+    return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<double> a, Tensor<float> b){
+    return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<double> a, Tensor<long> b){
+    return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<double> a, Tensor<int> b){
+    return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<double> a, Tensor<bool> b){
+    return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<float> a, Tensor<double> b){
+    return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<float> a, Tensor<float> b){
+    return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<float> a, Tensor<long> b){
+    return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<float> a, Tensor<int> b){
+    return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<float> a, Tensor<bool> b){
+    return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<long> a, Tensor<double> b){
+    return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<long> a, Tensor<float> b){
+    return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<long> Min(this Tensor<long> a, Tensor<long> b){
+    return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<long> Min(this Tensor<long> a, Tensor<int> b){
+    return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<long> Min(this Tensor<long> a, Tensor<bool> b){
+    return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<int> a, Tensor<double> b){
+    return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<int> a, Tensor<float> b){
+    return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<long> Min(this Tensor<int> a, Tensor<long> b){
+    return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<int> Min(this Tensor<int> a, Tensor<int> b){
+    return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<int> Min(this Tensor<int> a, Tensor<bool> b){
+    return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<double> Min(this Tensor<bool> a, Tensor<double> b){
+    return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<float> Min(this Tensor<bool> a, Tensor<float> b){
+    return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<long> Min(this Tensor<bool> a, Tensor<long> b){
+    return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<int> Min(this Tensor<bool> a, Tensor<int> b){
+    return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => x > y ? y : x);
+}
+public static Tensor<bool> Min(this Tensor<bool> a, Tensor<bool> b){
+    return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x > y ? y : x);
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Mod.csg b/csharp/CodeGenerator/Results/Mod.csg
new file mode 100644
index 0000000..8e52f39
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Mod.csg
@@ -0,0 +1,76 @@
+
+public static Tensor<double> Mod(this Tensor<double> a, Tensor<double> b){
+    return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<double> a, Tensor<float> b){
+    return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<double> a, Tensor<long> b){
+    return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<double> a, Tensor<int> b){
+    return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<double> a, Tensor<bool> b){
+    return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<float> a, Tensor<double> b){
+    return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<float> a, Tensor<float> b){
+    return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<float> a, Tensor<long> b){
+    return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<float> a, Tensor<int> b){
+    return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<float> a, Tensor<bool> b){
+    return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<long> a, Tensor<double> b){
+    return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<long> a, Tensor<float> b){
+    return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<long> Mod(this Tensor<long> a, Tensor<long> b){
+    return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x % y);
+}
+public static Tensor<long> Mod(this Tensor<long> a, Tensor<int> b){
+    return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x % y);
+}
+public static Tensor<long> Mod(this Tensor<long> a, Tensor<bool> b){
+    return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<int> a, Tensor<double> b){
+    return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<int> a, Tensor<float> b){
+    return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<long> Mod(this Tensor<int> a, Tensor<long> b){
+    return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x % y);
+}
+public static Tensor<int> Mod(this Tensor<int> a, Tensor<int> b){
+    return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x % y);
+}
+public static Tensor<int> Mod(this Tensor<int> a, Tensor<bool> b){
+    return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x % y);
+}
+public static Tensor<double> Mod(this Tensor<bool> a, Tensor<double> b){
+    return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x % y);
+}
+public static Tensor<float> Mod(this Tensor<bool> a, Tensor<float> b){
+    return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x % y);
+}
+public static Tensor<long> Mod(this Tensor<bool> a, Tensor<long> b){
+    return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => x % y);
+}
+public static Tensor<int> Mod(this Tensor<bool> a, Tensor<int> b){
+    return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => x % y);
+}
+public static Tensor<bool> Mod(this Tensor<bool> a, Tensor<bool> b){
+    return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x % y);
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Pow.csg b/csharp/CodeGenerator/Results/Pow.csg
new file mode 100644
index 0000000..d28c503
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Pow.csg
@@ -0,0 +1,31 @@
+
+public static Tensor<double> Pow(Tensor<double> inp, int y){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Pow(x, y));
+}
+public static Tensor<float> Pow(Tensor<float> inp, int y){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Pow(x, y));
+}
+public static Tensor<long> Pow(Tensor<long> inp, int y){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Pow(x, y));
+}
+public static Tensor<int> Pow(Tensor<int> inp, int y){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Pow(x, y));
+}
+public static Tensor<bool> Pow(Tensor<bool> inp, int y){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Pow(x, y));
+}
+public static Tensor<double> Pow(int y, Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Pow(y, x));
+}
+public static Tensor<float> Pow(int y, Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Pow(y, x));
+}
+public static Tensor<long> Pow(int y, Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Pow(y, x));
+}
+public static Tensor<int> Pow(int y, Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Pow(y, x));
+}
+public static Tensor<bool> Pow(int y, Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Pow(y, x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Round.csg b/csharp/CodeGenerator/Results/Round.csg
new file mode 100644
index 0000000..0e260e4
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Round.csg
@@ -0,0 +1,16 @@
+
+public static Tensor<double> Round(this Tensor<double> inp){
+    return OnElemOperation.Execute<double, double>(inp, x => System.Math.Round(x));
+}
+public static Tensor<float> Round(this Tensor<float> inp){
+    return OnElemOperation.Execute<float, float>(inp, x => System.Math.Round(x));
+}
+public static Tensor<long> Round(this Tensor<long> inp){
+    return OnElemOperation.Execute<long, long>(inp, x => System.Math.Round(x));
+}
+public static Tensor<int> Round(this Tensor<int> inp){
+    return OnElemOperation.Execute<int, int>(inp, x => System.Math.Round(x));
+}
+public static Tensor<bool> Round(this Tensor<bool> inp){
+    return OnElemOperation.Execute<bool, bool>(inp, x => System.Math.Round(x));
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Results/Sub.csg b/csharp/CodeGenerator/Results/Sub.csg
new file mode 100644
index 0000000..593a3eb
--- /dev/null
+++ b/csharp/CodeGenerator/Results/Sub.csg
@@ -0,0 +1,76 @@
+
+public static Tensor<double> Sub(this Tensor<double> a, Tensor<double> b){
+    return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<double> a, Tensor<float> b){
+    return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<double> a, Tensor<long> b){
+    return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<double> a, Tensor<int> b){
+    return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<double> a, Tensor<bool> b){
+    return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<float> a, Tensor<double> b){
+    return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<float> a, Tensor<float> b){
+    return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<float> a, Tensor<long> b){
+    return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<float> a, Tensor<int> b){
+    return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<float> a, Tensor<bool> b){
+    return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<long> a, Tensor<double> b){
+    return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<long> a, Tensor<float> b){
+    return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<long> Sub(this Tensor<long> a, Tensor<long> b){
+    return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x - y);
+}
+public static Tensor<long> Sub(this Tensor<long> a, Tensor<int> b){
+    return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x - y);
+}
+public static Tensor<long> Sub(this Tensor<long> a, Tensor<bool> b){
+    return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<int> a, Tensor<double> b){
+    return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<int> a, Tensor<float> b){
+    return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<long> Sub(this Tensor<int> a, Tensor<long> b){
+    return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x - y);
+}
+public static Tensor<int> Sub(this Tensor<int> a, Tensor<int> b){
+    return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x - y);
+}
+public static Tensor<int> Sub(this Tensor<int> a, Tensor<bool> b){
+    return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x - y);
+}
+public static Tensor<double> Sub(this Tensor<bool> a, Tensor<double> b){
+    return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x - y);
+}
+public static Tensor<float> Sub(this Tensor<bool> a, Tensor<float> b){
+    return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x - y);
+}
+public static Tensor<long> Sub(this Tensor<bool> a, Tensor<long> b){
+    return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => x - y);
+}
+public static Tensor<int> Sub(this Tensor<bool> a, Tensor<int> b){
+    return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => x - y);
+}
+public static Tensor<bool> Sub(this Tensor<bool> a, Tensor<bool> b){
+    return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x - y);
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/StaticUtilGenerator.cs b/csharp/CodeGenerator/StaticUtilGenerator.cs
new file mode 100644
index 0000000..91eaa1b
--- /dev/null
+++ b/csharp/CodeGenerator/StaticUtilGenerator.cs
@@ -0,0 +1,44 @@
+using System.Text;
+using System.Collections.Generic;
+
+namespace CodeGenerator{
+    public class StaticUtilGenerator{
+        public string Name{ get; set; }
+        public string Expr{ get; set; }
+        public List<string> Parameters{ get; set; }
+        private List<string> _types= new List<string>(new string[]{ "double", "float", "long", "int", "bool" });
+        private string _targetType = null;
+        public string GetCode(){
+            StringBuilder builder = new StringBuilder();
+            foreach(var typeInp in _types){
+                string typeRes = _targetType??typeInp;
+                builder.Append(@"
+public static Tensor<" + typeRes + $"> {Name}(Tensor<" + typeInp + @"> inp" + (Parameters is null ? "": ", ") + 
+                                    (Parameters is null ? "": string.Join(',', Parameters)) + @"){
+    return OnElemOperation.Execute<" + $"{typeInp}, {typeRes}" + $">(inp, x => {Expr});" + @"
+}");
+            }
+            return builder.ToString();
+        }
+    }
+
+    public class ReversedStaticUtilGenerator{
+        public string Name{ get; set; }
+        public string Expr{ get; set; }
+        public List<string> Parameters{ get; set; }
+        private List<string> _types= new List<string>(new string[]{ "double", "float", "long", "int", "bool" });
+        private string _targetType = null;
+        public string GetCode(){
+            StringBuilder builder = new StringBuilder();
+            foreach(var typeInp in _types){
+                string typeRes = _targetType??typeInp;
+                builder.Append(@"
+public static Tensor<" + typeRes + $"> {Name}({(Parameters is null ? "": string.Join(',', Parameters))}, Tensor<" + typeInp + @"> inp"
+                                     + @"){
+    return OnElemOperation.Execute<" + $"{typeInp}, {typeRes}" + $">(inp, x => {Expr});" + @"
+}");
+            }
+            return builder.ToString();
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/CodeGenerator/Writer.cs b/csharp/CodeGenerator/Writer.cs
new file mode 100644
index 0000000..82d2368
--- /dev/null
+++ b/csharp/CodeGenerator/Writer.cs
@@ -0,0 +1,16 @@
+using System.IO;
+
+namespace CodeGenerator{
+    public class CodeWriter{
+        public static string Prefix { get; set; } = "Results";
+        public static void Write(string Name, string code){
+            Name += ".csg";
+            Name = Prefix + "/" + Name;
+            FileStream fs = new FileStream(Name, FileMode.OpenOrCreate, FileAccess.ReadWrite);
+            StreamWriter sw = new StreamWriter(fs);
+            sw.Write(code);
+            sw.Close();
+            fs.Close();
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Algebra/Matmul.cs b/csharp/Num.NET/Algebra/Matmul.cs
deleted file mode 100644
index 2aff2f6..0000000
--- a/csharp/Num.NET/Algebra/Matmul.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using Numnet.Common;
-using Numnet.Native;
-using Numnet.Exceptions;
-
-namespace Numnet.Algebra{
-    public static class MatmulExtension{
-
-        public static Tensor Matmul(this Tensor lhs, Tensor rhs)
-        {
-            TensorLayout leftLayout = new TensorLayout(lhs.TLayout);
-            TensorLayout rightLayout = new TensorLayout(rhs.TLayout);
-            Tensor res = new Tensor(DeduceLayout(leftLayout, rightLayout));
-            res.TLayout.InitContiguousLayout();
-            IntPtr status = Tensor.Execute(NativeApi.Matmul, lhs.TMemory, rhs.TMemory, res.TMemory, leftLayout, rightLayout, res.TLayout, IntPtr.Zero, Tensor.Provider);
-            NativeStatus.AssertOK(status);
-            return res;
-        }
-        private static TensorLayout DeduceLayout(TensorLayout lhs, TensorLayout rhs){
-            TensorLayout res = new TensorLayout();
-            res.DType = DefaultTypeDeduce.Deduce(lhs.DType, rhs.DType);
-            if (lhs.NDim <= 0 || rhs.NDim <= 0)
-                throw new MismatchedShapeException(lhs, rhs, "Matmul");
-            if (lhs.IsScalar() && rhs.IsScalar()) {
-                res.Shape[0] = 1;
-                res.NDim = 1;
-                return res;
-            }
-            if (lhs.NDim == 1 && rhs.NDim == 1) {
-                res.Shape[0] = rhs.Shape[0];
-                res.Shape[1] = lhs.Shape[0];
-                res.NDim = 2;
-                lhs.BroadcastInplace(new TensorShape(lhs.Shape[0], 1));
-                rhs.BroadcastInplace(new TensorShape(1, rhs.Shape[0]));
-                return res;
-            }
-            int dim = lhs.NDim > rhs.NDim ? lhs.NDim : rhs.NDim;
-            res.NDim = dim;
-            List<int> leftDstShape = new List<int>();
-            List<int> rightDstShape = new List<int>();
-            for (int i = dim - 1; i >= 2; i--) {
-                if (i >= lhs.NDim || i < rhs.NDim && lhs.Shape[i] == 1) {
-                    res.Shape[i] = rhs.Shape[i];
-                } else if (i >= rhs.NDim || i < lhs.NDim && rhs.Shape[i] == 1) {
-                    res.Shape[i] = lhs.Shape[i];
-                } else if (lhs.Shape[i] == rhs.Shape[i]) {
-                    res.Shape[i] = lhs.Shape[i];
-                } else {
-                    throw new MismatchedShapeException(lhs, rhs, "Matmul");
-                }
-                leftDstShape.Add(res.Shape[i]);
-                rightDstShape.Add(res.Shape[i]);
-            }
-            if (lhs.NDim == 1 && rhs.Shape[1] != lhs.Shape[0] ||
-                rhs.NDim == 1 && rhs.Shape[0] != lhs.Shape[0] ||
-                lhs.NDim != 1 && rhs.NDim != 1 && lhs.Shape[0] != rhs.Shape[1]) {
-                throw new MismatchedShapeException(lhs, rhs, "Matmul");
-            }
-            res.Shape[1] = lhs.NDim == 1 ? 1 : lhs.Shape[1];
-            res.Shape[0] = rhs.NDim == 1 ? 1 : rhs.Shape[0];
-            leftDstShape.Add(lhs.NDim > 1 ? lhs.Shape[1] : 1);
-            rightDstShape.Add(rhs.NDim > 1 ? rhs.Shape[1] : rhs.Shape[0]);
-            leftDstShape.Add(lhs.Shape[0]);
-            rightDstShape.Add(rhs.NDim > 1 ? rhs.Shape[0] : 1);
-            if (rhs.NDim == 1) {
-                rhs.Shape[1] = rhs.Shape[0];
-                rhs.Shape[0] = 1;
-                rhs.NDim = 2;
-                rhs.Stride[1] = 1;
-                rhs.Stride[0] = 0;
-            }
-            lhs.BroadcastInplace(new TensorShape(leftDstShape));
-            rhs.BroadcastInplace(new TensorShape(rightDstShape));
-            return res;
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Manipulation/Permute.cs b/csharp/Num.NET/Manipulation/Permute.cs
deleted file mode 100644
index d925f21..0000000
--- a/csharp/Num.NET/Manipulation/Permute.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Numnet.Common;
-using Numnet.Native;
-using Numnet.Exceptions;
-using Numnet.Native.Param;
-
-namespace Numnet.Manipulation{
-    public static class PermuteExtension{
-
-        public static Tensor Permute(this Tensor src, int[] dims)
-        {
-            Tensor res = new Tensor(DeduceLayout(src.TLayout, dims));
-            res.TLayout.InitContiguousLayout();
-            PermuteInternal(src, res, dims);
-            return res;
-        }
-        private unsafe static void PermuteInternal(Tensor src, Tensor dst, int[] dims){
-            fixed(int* pdims = dims){
-                PermuteParam p = new PermuteParam() { dims = new IntPtr(pdims) };
-                IntPtr status = Tensor.Execute(NativeApi.Permute, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor.Provider);
-                NativeStatus.AssertOK(status);
-            }
-        }
-        private static TensorLayout DeduceLayout(TensorLayout src, int[] dims){
-            TensorLayout res = new TensorLayout();
-            int duplicated = 0;
-            res.DType = src.DType;
-            res.NDim = src.NDim;
-            for (int i = 0; i < src.NDim; i++) {
-                if (dims[i] >= src.NDim) {
-                    throw new InvalidParamException("Invalid param for permute.");
-                }
-                res.Shape[dims[i]] = src.Shape[i];
-                if ((duplicated & (1 << dims[i])) != 0) {
-                    throw new InvalidParamException("Duplicated index in the param of permute.");
-                }
-                duplicated += 1 << dims[i];
-            }
-            return res;
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Manipulation/Transpose.cs b/csharp/Num.NET/Manipulation/Transpose.cs
deleted file mode 100644
index 60c9778..0000000
--- a/csharp/Num.NET/Manipulation/Transpose.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using Numnet.Common;
-using Numnet.Native;
-using Numnet.Exceptions;
-using Numnet.Native.Param;
-
-namespace Numnet.Manipulation{
-    public static class TransposeExtension{
-
-        public static Tensor Transpose(this Tensor src, int dimA, int dimB)
-        {
-            Tensor res = new Tensor(DeduceLayout(src.TLayout, dimA, dimB));
-            res.TLayout.InitContiguousLayout();
-            TransposeInternal(src, res, dimA, dimB);
-            return res;
-        }
-        private unsafe static void TransposeInternal(Tensor src, Tensor dst, int dimA, int dimB){
-            TransposeParam param = new TransposeParam() { dimA = dimA, dimB = dimB };
-            IntPtr status = Tensor.Execute(NativeApi.Transpose, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&param), Tensor.Provider);
-            NativeStatus.AssertOK(status);
-        }
-        private static TensorLayout DeduceLayout(TensorLayout src, int dimA, int dimB){
-            TensorLayout res = new TensorLayout();
-            if (dimA >= src.NDim || dimB >= src.NDim) {
-                throw new InvalidParamException("Invalid param for transpose.");
-            }
-            res.DType = src.DType;
-            res.NDim = src.NDim;
-            for (int i = 0; i < src.NDim; i++) {
-                res.Shape[i] = src.Shape[i];
-            }
-            res.Shape[src.NDim - dimA - 1] = src.Shape[src.NDim - dimB - 1];
-            res.Shape[src.NDim - dimB - 1] = src.Shape[src.NDim - dimA - 1];
-            return res;
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Native/NativeApi.cs b/csharp/Num.NET/Native/NativeApi.cs
deleted file mode 100644
index 7e6c0aa..0000000
--- a/csharp/Num.NET/Native/NativeApi.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Numnet.Native{
-    internal static class NativeApi{
-        [DllImport("/home/lyh/code/Num.NET/build/apis/libnumnet.so")]
-        public static extern StatusCode GetErrorCode(IntPtr status);
-        [DllImport("/home/lyh/code/Num.NET/build/apis/libnumnet.so")]
-        public static extern IntPtr GetErrorMessage(IntPtr status);
-        [DllImport("/home/lyh/code/Num.NET/build/apis/libnumnet.so")]
-        public static extern void FreeStatusMemory(IntPtr status);
-
-
-        [DllImport("/home/lyh/code/Num.NET/build/apis/libnumnet.so")]
-        public static extern IntPtr Matmul(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
-        [DllImport("/home/lyh/code/Num.NET/build/apis/libnumnet.so")]
-        public static extern IntPtr Permute(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
-        [DllImport("/home/lyh/code/Num.NET/build/apis/libnumnet.so")]
-        public static extern IntPtr Transpose(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Native/NativeParam.cs b/csharp/Num.NET/Native/NativeParam.cs
deleted file mode 100644
index 4edb39b..0000000
--- a/csharp/Num.NET/Native/NativeParam.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Numnet.Exceptions;
-
-namespace Numnet.Native.Param{
-    internal struct MatmulParam{
-        
-    }
-
-    internal struct PermuteParam{
-        internal IntPtr dims;
-    }
-
-    internal struct TransposeParam{
-        internal int dimA;
-        internal int dimB;
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Program.cs b/csharp/Num.NET/Program.cs
deleted file mode 100644
index 7797e32..0000000
--- a/csharp/Num.NET/Program.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-﻿using System.Runtime.InteropServices;
-using Numnet.Native;
-using Numnet.Native.Param;
-using Numnet;
-using Numnet.Algebra;
-using Numnet.Manipulation;
-
-unsafe{
-    Tensor<int> a = Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[] { 1, 3, 2 });
-    Tensor<double> b = Tensor.FromArray<double>(new double[]{1.2, 2.6, 3.9, 4.1, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6, 1.2, 2.6, 
-                        3.9, 4.1, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6}, new int[] { 4, 2, 4 });
-    a.BroadcastTo(new int[]{4, 3, 2});
-    Console.WriteLine(a);
-    // int[] permuteParam = new int[3] { 2, 0, 1 };
-    // var ap = Tensor.Zeros<int>(new int[] { 2, 4, 3 });
-    // fixed(int* p = permuteParam){
-    //     var param = new Permute() { dims = new IntPtr(p) };
-    //     var ptr = new IntPtr(&param);
-    //     IntPtr st = Tensor<int>.Execute(a, ap, NativeApi.Permute, ptr, NativeProvider.Naive);
-    //     Console.WriteLine(NativeStatus.GetErrorCode(st));
-    //     Console.WriteLine(NativeStatus.GetErrorMessage(st)); 
-    // }
-    // Console.WriteLine(ap);
-    
-    // Tensor<double> c = Tensor.Zeros<double>(new int[] { 4, 3, 4 });
-    // IntPtr status = Tensor<int>.Execute(a, b, c, NativeApi.Matmul, IntPtr.Zero, NativeProvider.Naive);
-    Tensor c = a.Matmul(b);
-    // Console.WriteLine(NativeStatus.GetErrorCode(status));
-    // Console.WriteLine(NativeStatus.GetErrorMessage(status));
-    Console.WriteLine(c);
-    c = c.Reshape(new int[] { 6, 8 });
-    Console.WriteLine(c);
-    c = c.Permute(new int[] { 1, 0 });
-    Console.WriteLine(c);
-    c = c.Transpose(0, 1);
-    Console.WriteLine(c);
-    c[0, 0] = 999.0;
-    c[5, 7] = -888.0;
-    Console.WriteLine(c);
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Common/BroadCast.cs b/csharp/Num.NET/Tensor/Common/BroadCast.cs
deleted file mode 100644
index 815c644..0000000
--- a/csharp/Num.NET/Tensor/Common/BroadCast.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Numnet.Common;
-
-namespace Numnet{
-    public partial class Tensor{
-        // public Tensor<T> BroadCast(TensorShape targetShape){
-        //     return new Tensor<T>(this.TMemory, this.TLayout.BroadcastTo(targetShape));
-        // }
-
-        // public Tensor<T> BroadCast(int[] targetShape){
-        //     return new Tensor<T>(this.TMemory, this.TLayout.BroadcastTo(new TensorShape(targetShape)));
-        // }
-        public Tensor Broadcast(TensorShape targetShape){
-            return new Tensor(this.TMemory, this.TLayout.Broadcast(targetShape));
-        }
-
-        public Tensor Broadcast(int[] targetShape){
-            return new Tensor(this.TMemory, this.TLayout.Broadcast(new TensorShape(targetShape)));
-        }
-        public virtual void BroadcastTo(TensorShape targetShape){
-            TLayout.BroadcastInplace(targetShape);
-        }
-        public virtual void BroadcastTo(int[] targetShape){
-            TLayout.BroadcastInplace(new TensorShape(targetShape));
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Common/Reshape.cs b/csharp/Num.NET/Tensor/Common/Reshape.cs
deleted file mode 100644
index 39d62a3..0000000
--- a/csharp/Num.NET/Tensor/Common/Reshape.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Numnet.Common;
-
-namespace Numnet{
-    public partial class Tensor{
-        // public Tensor<T> Reshape(int[] shape){
-        //     return new Tensor<T>(this.TMemory, this.TLayout.Reshape(new TensorShape(shape), false));
-        // }
-        // public Tensor<T> Reshape(TensorShape shape){
-        //     return new Tensor<T>(this.TMemory, this.TLayout.Reshape(shape, false));
-        // }
-        public Tensor Reshape(int[] shape){
-            return new Tensor(this.TMemory, this.TLayout.Reshape(new TensorShape(shape), false));
-        }
-        public Tensor Reshape(TensorShape shape){
-            return new Tensor(this.TMemory, this.TLayout.Reshape(shape, false));
-        }
-
-        public void ReshapeTo(int[] shape){
-            TLayout = TLayout.Reshape(new TensorShape(shape), false);
-        }
-        public void ReshapeTo(TensorShape shape){
-            TLayout = TLayout.Reshape(shape, false);
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Common/TensorMemory.cs b/csharp/Num.NET/Tensor/Common/TensorMemory.cs
deleted file mode 100644
index d6cb79f..0000000
--- a/csharp/Num.NET/Tensor/Common/TensorMemory.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Runtime.InteropServices;
-using System.Buffers;
-using System.Linq;
-using Numnet.Native;
-
-namespace Numnet.Common{
-    internal class TensorMemory{
-        private Memory<byte> _memory;
-        public TensorMemory(int length, DType dtype){
-            var size = TensorTypeInfo.GetTypeSize(dtype);
-            _memory = new byte[length * size];
-        }
-        public Span<T> AsSpan<T>() where T: struct{
-            return MemoryMarshal.Cast<byte, T>(_memory.Span);
-        }
-        internal Span<byte> AsSpan(){
-            return _memory.Span;
-        }
-        public void Pin(out MemoryHandle handle){
-            handle = _memory.Pin();
-        }
-    }
-    internal class TensorMemory<T>:TensorMemory where T :struct
-    {
-        public TensorMemory(T[] data):base(data.Length, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype){
-            data.CopyTo(AsSpan<T>());
-        }
-        public TensorMemory(Span<T> data):base(data.Length, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype){
-            data.CopyTo(AsSpan<T>());
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Tensor.cs b/csharp/Num.NET/Tensor/Tensor.cs
deleted file mode 100644
index 0ca524e..0000000
--- a/csharp/Num.NET/Tensor/Tensor.cs
+++ /dev/null
@@ -1,309 +0,0 @@
-using System.Buffers;
-using Numnet.Native;
-using System.Text;
-using Numnet.Common;
-using Numnet.Exceptions;
-using System.Runtime.InteropServices;
-
-namespace Numnet{
-    public partial class Tensor
-    {
-        internal delegate IntPtr DoubleInputOperation(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
-        internal delegate IntPtr SingleInputOperation(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
-        public TensorLayout TLayout{get; protected set; }
-        internal TensorMemory TMemory{ get; set; }
-        public static NativeProvider Provider { get; set; } = NativeProvider.Naive;
-        protected void Pin(out MemoryHandle handle){
-            TMemory.Pin(out handle);
-        }
-        internal Tensor(TensorMemory memory, TensorLayout layout){
-            TMemory = memory;
-            TLayout = layout;
-        }
-        internal Tensor(TensorLayout layout){
-            TLayout = layout;
-            TMemory = new TensorMemory(layout.TotalElemCount(), layout.DType);
-        }
-        internal Tensor(TensorShape shape, DType dtype){
-            TLayout = new TensorLayout(shape, dtype);
-            TMemory = new TensorMemory(shape.TotalElemCount(), dtype);
-        }
-        internal int IndicesToPosition(params int[] indices){
-            if(indices.Length != TLayout.NDim){
-                throw new InvalidArgumentException($"Index does not have same dims with tensor, " + 
-                    "the index is {indices.Length} dims but the tensor is {TLayout.NDim}.");
-            }
-            int res = 0;
-            for (int i = 0; i < TLayout.NDim; i++) {
-                if(indices[i] < -1 || indices[i] >= TLayout.Shape[i]){
-                    throw new InvalidArgumentException($"{i}th dim of the index exceeds the bound of the shape.");
-                }
-                res += indices[i] * TLayout.Stride[i];
-            }
-            return res + TLayout.Offset;
-        }
-        internal ValueType GetValue(int idx){
-            if(TLayout.DType == DType.Int32){
-                return AsSpan<int>()[idx];
-            }
-            else if(TLayout.DType == DType.Int64){
-                return AsSpan<long>()[idx];
-            }
-            else if(TLayout.DType == DType.Float32){
-                return AsSpan<float>()[idx];
-            }
-            else if(TLayout.DType == DType.Float64){
-                return AsSpan<double>()[idx];
-            }
-            else if(TLayout.DType == DType.Bool){
-                return AsSpan<bool>()[idx];
-            }
-            else{
-                throw new NotImplementedException();
-            }
-        }
-        internal void SetValue(int idx, ValueType value){
-            if(TLayout.DType == DType.Int32){
-                AsSpan<int>()[idx] = (int)value;
-            }
-            else if(TLayout.DType == DType.Int64){
-                AsSpan<long>()[idx] = (long)value;
-            }
-            else if(TLayout.DType == DType.Float32){
-                AsSpan<float>()[idx] = (float)value;
-            }
-            else if(TLayout.DType == DType.Float64){
-                AsSpan<double>()[idx] = (double)value;
-            }
-            else if(TLayout.DType == DType.Bool){
-                AsSpan<bool>()[idx] = (bool)value;
-            }
-            else{
-                throw new NotImplementedException();
-            }
-        }
-        public Span<T> AsSpan<T>() where T:struct{
-            return TMemory.AsSpan<T>();
-        }
-        public Tensor<T> To<T>() where T:struct{
-            if(TensorTypeInfo.GetTypeInfo(typeof(T))._dtype != TLayout.DType){
-                throw new NotImplementedException();
-            }
-            return new Tensor<T>(this);
-        }
-        public bool IsType<T>(){
-            return TLayout.DType == TensorTypeInfo.GetTypeInfo(typeof(T))._dtype;
-        }
-        public bool IsType(DType dtype){
-            return TLayout.DType == dtype;
-        }
-        public ValueType this[params int[] index]{
-            get{
-                Array.Reverse(index);
-                return GetValue(IndicesToPosition(index));
-            }
-            set{
-                Array.Reverse(index);
-                SetValue(IndicesToPosition(index), value);
-            }
-        }
-        unsafe internal static IntPtr Execute(DoubleInputOperation func, TensorMemory a, TensorMemory b, TensorMemory oup, 
-                            TensorLayout layoutA, TensorLayout layoutB, TensorLayout layoutOup, IntPtr param, NativeProvider provider){
-            MemoryHandle handleA, handleB, handleOup;
-            a.Pin(out handleA);
-            b.Pin(out handleB);
-            oup.Pin(out handleOup);
-            IntPtr status;
-            fixed(int* shapeAPtr = layoutA.Shape, shapeBPtr = layoutB.Shape, shapeOupPtr = layoutOup.Shape, 
-                        strideAPtr = layoutA.Stride, strideBPtr = layoutB.Stride, strideOupPtr = layoutOup.Stride){
-                NativeTensor nativeA = new NativeTensor()
-                {
-                    dtype = layoutA.DType,
-                    ndim = layoutA.NDim,
-                    offset = layoutA.Offset,
-                    shape = new IntPtr(shapeAPtr),
-                    stride = new IntPtr(strideAPtr),
-                    data = new IntPtr(handleA.Pointer)
-                };
-                NativeTensor nativeB = new NativeTensor()
-                {
-                    dtype = layoutB.DType,
-                    ndim = layoutB.NDim,
-                    offset = layoutB.Offset,
-                    shape = new IntPtr(shapeBPtr),
-                    stride = new IntPtr(strideBPtr),
-                    data = new IntPtr(handleB.Pointer)
-                };
-                NativeTensor nativeOup = new NativeTensor()
-                {
-                    dtype = layoutOup.DType,
-                    ndim = layoutOup.NDim,
-                    offset = layoutOup.Offset,
-                    shape = new IntPtr(shapeOupPtr),
-                    stride = new IntPtr(strideOupPtr),
-                    data = new IntPtr(handleOup.Pointer)
-                };
-                status = func(new IntPtr(&nativeA), new IntPtr(&nativeB), new IntPtr(&nativeOup), param, provider);
-            }
-            handleA.Dispose();
-            handleB.Dispose();
-            handleOup.Dispose();
-            return status;
-        }
-
-        unsafe internal static IntPtr Execute(SingleInputOperation func, TensorMemory inp, TensorMemory oup, TensorLayout layoutInp, 
-                                                    TensorLayout layoutOup, IntPtr param, NativeProvider provider){
-            MemoryHandle handleInp, handleOup;
-            inp.Pin(out handleInp);
-            oup.Pin(out handleOup);
-            IntPtr status;
-            fixed(int* shapeInpPtr = layoutInp.Shape, shapeOupPtr = layoutOup.Shape, 
-                        strideInpPtr = layoutInp.Stride, strideOupPtr = layoutOup.Stride){
-                NativeTensor nativeInp = new NativeTensor()
-                {
-                    dtype = layoutInp.DType,
-                    ndim = layoutInp.NDim,
-                    offset = layoutInp.Offset,
-                    shape = new IntPtr(shapeInpPtr),
-                    stride = new IntPtr(strideInpPtr),
-                    data = new IntPtr(handleInp.Pointer)
-                };
-                NativeTensor nativeOup = new NativeTensor()
-                {
-                    dtype = layoutOup.DType,
-                    ndim = layoutOup.NDim,
-                    offset = layoutOup.Offset,
-                    shape = new IntPtr(shapeOupPtr),
-                    stride = new IntPtr(strideOupPtr),
-                    data = new IntPtr(handleOup.Pointer)
-                };
-                status = func(new IntPtr(&nativeInp), new IntPtr(&nativeOup), param, provider);
-            }
-            handleInp.Dispose();
-            handleOup.Dispose();
-            return status;
-        }
-
-        public override string ToString()
-        {
-            Func<int, int> getRealPos = idx => {
-                int res = 0;
-                for (int i =  TLayout.NDim - 1; i >= 0; i--) {
-                    int mod = TLayout.Stride[i];
-                    if (mod <= 0){
-                        int j = i - 1;
-                        while(j >= 0 && TLayout.Stride[j] <= 0) j--;
-                        if(j < 0){
-                            mod = 1;
-                        }
-                        else{
-                            mod = TLayout.Stride[j] * TLayout.Shape[j];
-                        }
-                    }
-                    else
-                        res += idx / mod * mod;
-                    idx %= mod;
-                    if (idx <= 0) break;
-                }
-                return res;
-            };
-
-            StringBuilder r = new StringBuilder($"Tensor({TLayout.GetInfoString()}):\n");
-            for (int i = 0; i < TLayout.TotalElemCount(); i++) {
-                int mod = 1;
-                for (int j = 0; j < TLayout.NDim; j++) {
-                    mod *= TLayout.Shape[j];
-                    if (i % mod == 0) {
-                        r.Append("[");
-                    } else {
-                        break;
-                    }
-                }
-                r.Append(" ").Append(GetValue(getRealPos(i)));
-
-                if ((i + 1) % TLayout.Shape[0] != 0) r.Append(",");
-
-                r.Append(" ");
-                mod = 1;
-                int hit_times = 0;
-                for (int j = 0; j < TLayout.NDim; j++) {
-                    mod *= TLayout.Shape[j];
-                    if ((i + 1) % mod == 0) {
-                        r.Append("]");
-                        hit_times++;
-                    } else {
-                        break;
-                    }
-                }
-                if (hit_times > 0 && hit_times < TLayout.NDim) {
-                    r.Append(",\n");
-                    for (int j = 0; j < TLayout.NDim - hit_times; j++) {
-                        r.Append(" ");
-                    }
-                }
-            }
-            // r.Append("\n");
-            return r.ToString();
-        }
-    }
-
-    public sealed partial class Tensor<T>: Tensor where T:struct{
-        internal Tensor(TensorMemory memory, TensorLayout layout):base(memory, layout){ }
-        internal Tensor(TensorLayout layout) : base(layout) { }
-
-        internal Tensor(Tensor source):base(source.TMemory, source.TLayout){
-
-        }
-
-        public new T this[params int[] index]{
-            get{
-                Array.Reverse(index);
-                return AsSpan<T>()[IndicesToPosition(index)];
-            }
-            set{
-                Array.Reverse(index);
-                AsSpan<T>()[IndicesToPosition(index)] = value;
-            }
-        }
-        // public Tensor(IEnumerable<T> data, Span<int> shape):base(new TensorLayout(TensorTypeInfo.GetTypeInfo(typeof(T))._dtype, shape)){
-        //     TMemory = new TensorMemory<T>(data.ToArray());
-        // }
-
-        // public Tensor(T[] data, Span<int> shape):base(new TensorLayout(TensorTypeInfo.GetTypeInfo(typeof(T))._dtype, shape)){
-        //     if(data.Length < TLayout.TotalElemCount()){
-        //         // TODO
-        //         throw new Exception();
-        //     }
-        //     TMemory = new TensorMemory<T>(data);
-        // }
-
-        // public Tensor(Array data):base(new TensorLayout()){
-        //     var ndim = data.Rank;
-        //     int[] shape = new int[ndim];
-        //     for (int i = 0; i < ndim; i++){
-        //         shape[i] = data.GetLength(i);
-        //     }
-        //     var dtypeInfo = TensorTypeInfo.GetTypeInfo(data.GetType().GetElementType()!);
-        //     TLayout.NDim = ndim;
-        //     TLayout.DType = dtypeInfo._dtype;
-        //     TLayout.Shape = shape;
-        //     TLayout.InitContiguousLayout();
-        //     TMemory = new TensorMemory<T>(data);
-        // }
-
-        // internal Tensor(int[] shape):base(new TensorLayout(TensorTypeInfo.GetTypeInfo(typeof(T))._dtype, shape)){
-        //     int length = 1;
-        //     foreach(var s in shape){
-        //         length *= s;
-        //     }
-        //     TMemory = new TensorMemory<T>(length);
-        // }
-
-        // internal Tensor(TensorMemory<T> memory, TensorLayout layout):base(layout){
-        //     if(TensorTypeInfo.GetTypeInfo(typeof(T))._dtype != layout.DType){
-        //         throw new NotImplementedException();
-        //     }
-        //     TMemory = memory;
-        // }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Utilities/TensorGeneration.cs b/csharp/Num.NET/Tensor/Utilities/TensorGeneration.cs
deleted file mode 100644
index f7ef5b5..0000000
--- a/csharp/Num.NET/Tensor/Utilities/TensorGeneration.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System.Buffers;
-using System.Runtime.InteropServices;
-using Numnet.Native;
-using Numnet.Common;
-
-namespace Numnet{
-    public partial class Tensor
-    {
-        public static Tensor Zeros(int[] shape, DType dtype){
-            Tensor res = new Tensor(new TensorLayout(shape, dtype));
-            res.TMemory.AsSpan().Fill(0);
-            return res;
-        }
-        public static Tensor<T> Zeros<T>(int[] shape)where T:struct{
-            Tensor res = new Tensor(new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
-            res.TMemory.AsSpan().Fill(0);
-            return res.To<T>();
-        }
-        public static Tensor<T> FromSpan<T>(Span<T> data, TensorShape shape)where T:struct{
-            if(shape.TotalElemCount() != data.Length){
-                // TODO
-                throw new Exception();
-            }
-            TensorMemory<T> memory = new TensorMemory<T>(data);
-            return new Tensor<T>(memory, new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
-        }
-        public static Tensor<T> FromArray<T>(T[] data, TensorShape shape)where T:struct{
-            if(shape.TotalElemCount() != data.Length){
-                // TODO
-                throw new Exception();
-            }
-            TensorMemory<T> memory = new TensorMemory<T>(data);
-            return new Tensor<T>(memory, new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
-        }
-        public static Tensor<T> FromArray<T>(T[] data, int[] shape)where T:struct{
-            TensorLayout layout = new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
-            if(layout.TotalElemCount() != data.Length){
-                // TODO
-                throw new Exception();
-            }
-            TensorMemory<T> memory = new TensorMemory<T>(data);
-            return new Tensor<T>(memory, layout);
-        }
-    }
-}
\ No newline at end of file
diff --git a/csharp/Num.NET.sln b/csharp/Tensor.NET.sln
similarity index 67%
rename from csharp/Num.NET.sln
rename to csharp/Tensor.NET.sln
index 06dac8a..b0fa4c7 100644
--- a/csharp/Num.NET.sln
+++ b/csharp/Tensor.NET.sln
@@ -3,10 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio Version 16
 VisualStudioVersion = 16.0.30114.105
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Num.NET", "Num.NET\Num.NET.csproj", "{24DFAC2B-3D5D-43ED-AC56-0F5A84E819BF}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tensor.NET", "Tensor.NET\Tensor.NET.csproj", "{24DFAC2B-3D5D-43ED-AC56-0F5A84E819BF}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensorBasicTest", "TensorBasicTest\TensorBasicTest.csproj", "{95FC7971-7C2D-4606-9044-C21FACB72048}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TensorOpTest", "TensorOpTest\TensorOpTest.csproj", "{36E4E68B-E2A7-4F15-92ED-AE550FA8969D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -24,5 +26,9 @@ Global
 		{95FC7971-7C2D-4606-9044-C21FACB72048}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{95FC7971-7C2D-4606-9044-C21FACB72048}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{95FC7971-7C2D-4606-9044-C21FACB72048}.Release|Any CPU.Build.0 = Release|Any CPU
+		{36E4E68B-E2A7-4F15-92ED-AE550FA8969D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{36E4E68B-E2A7-4F15-92ED-AE550FA8969D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{36E4E68B-E2A7-4F15-92ED-AE550FA8969D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{36E4E68B-E2A7-4F15-92ED-AE550FA8969D}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 EndGlobal
diff --git a/csharp/Tensor.NET/Algebra/Dot.cs b/csharp/Tensor.NET/Algebra/Dot.cs
new file mode 100644
index 0000000..fdb2338
--- /dev/null
+++ b/csharp/Tensor.NET/Algebra/Dot.cs
@@ -0,0 +1,249 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    public static class DotExtension{
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<T> Dot<T>(this Tensor<T> lhs, Tensor<T> rhs) where T : struct, IEquatable<T>, IConvertible{
+            return DotInternal<T, T, T>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<double> lhs, Tensor<int> rhs){
+            return DotInternal<double, int, double>(lhs, rhs);
+        }/// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<double> lhs, Tensor<float> rhs){
+            return DotInternal<double, float, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<double> lhs, Tensor<long> rhs){
+            return DotInternal<double, long, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<double> lhs, Tensor<bool> rhs){
+            return DotInternal<double, bool, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<int> lhs, Tensor<double> rhs){
+            return DotInternal<int, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Dot(this Tensor<int> lhs, Tensor<float> rhs){
+            return DotInternal<int, float, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Dot(this Tensor<int> lhs, Tensor<long> rhs){
+            return DotInternal<int, long, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<int> Dot(this Tensor<int> lhs, Tensor<bool> rhs){
+            return DotInternal<int, bool, int>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<long> lhs, Tensor<double> rhs){
+            return DotInternal<long, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Dot(this Tensor<long> lhs, Tensor<float> rhs){
+            return DotInternal<long, float, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Dot(this Tensor<long> lhs, Tensor<int> rhs){
+            return DotInternal<long, int, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Dot(this Tensor<long> lhs, Tensor<bool> rhs){
+            return DotInternal<long, bool, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Dot(this Tensor<float> lhs, Tensor<long> rhs){
+            return DotInternal<float, long, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Dot(this Tensor<float> lhs, Tensor<int> rhs){
+            return DotInternal<float, int, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Dot(this Tensor<float> lhs, Tensor<bool> rhs){
+            return DotInternal<float, bool, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<float> lhs, Tensor<double> rhs){
+            return DotInternal<float, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Dot(this Tensor<bool> lhs, Tensor<double> rhs){
+            return DotInternal<bool, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Dot(this Tensor<bool> lhs, Tensor<float> rhs){
+            return DotInternal<bool, float, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Dot(this Tensor<bool> lhs, Tensor<long> rhs){
+            return DotInternal<bool, long, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<int> Dot(this Tensor<bool> lhs, Tensor<int> rhs){
+            return DotInternal<bool, int, int>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the dot result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.dot.html#numpy.dot
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        private static Tensor<TC> DotInternal<TA, TB, TC>(Tensor<TA> lhs, Tensor<TB> rhs) 
+        where TA : struct, IEquatable<TA>, IConvertible
+        where TB : struct, IEquatable<TB>, IConvertible
+        where TC : struct, IEquatable<TC>, IConvertible
+        {
+            TensorLayout leftLayout = new TensorLayout(lhs.TLayout);
+            TensorLayout rightLayout = new TensorLayout(rhs.TLayout);
+            Tensor<TC> res = new Tensor<TC>(DeduceLayout(leftLayout, rightLayout));
+            res.TLayout.InitContiguousLayout();
+            IntPtr status = NativeExecutor.Execute(NativeApi.Dot, lhs.TMemory, rhs.TMemory, res.TMemory, leftLayout, rightLayout, res.TLayout, IntPtr.Zero, Tensor<TC>.Provider);
+            NativeStatus.AssertOK(status);
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout lhs, TensorLayout rhs){
+            TensorLayout res = new TensorLayout();
+            res.DType = DefaultTypeDeduce.Deduce(lhs.DType, rhs.DType);
+            if (lhs.NDim <= 0 || rhs.NDim <= 0)
+                throw new MismatchedShapeException(lhs, rhs, "Dot");
+            int dim = System.Math.Max(lhs.NDim, rhs.NDim);
+            for (int i = 0, j = 0, k = 0; i < lhs.NDim && j < rhs.NDim; i++, j++, k++) {
+                int a_idx = lhs.NDim - i - 1;
+                int b_idx = lhs.NDim - j - 1;
+                if (lhs.Shape[a_idx] != rhs.Shape[b_idx] && lhs.Shape[a_idx] != 1 &&
+                    rhs.Shape[b_idx] != 1) {
+                    throw new MismatchedShapeException("Cannot broadcast bool index to the shape of target tensor.");
+                } else if (lhs.Shape[a_idx] == rhs.Shape[b_idx]) {
+                    res.Shape[dim - k - 1] = lhs.Shape[a_idx];
+                } else if (lhs.Shape[a_idx] == 1) {
+                    res.Shape[dim - k - 1] = lhs.Shape[b_idx];
+                } else if (rhs.Shape[a_idx] == 1) {
+                    res.Shape[dim - k - 1] = lhs.Shape[a_idx];
+                } else {
+                    throw new MismatchedShapeException("Unknown error when deducing the layout.");
+                }
+            }
+            for (int i = System.Math.Min(lhs.NDim, rhs.NDim); i < lhs.NDim; i++) {
+                res.Shape[dim - i - 1] = lhs.Shape[lhs.NDim - i - 1];
+            }
+            for (int i = System.Math.Min(lhs.NDim, rhs.NDim); i < rhs.NDim; i++) {
+                res.Shape[dim - i - 1] = rhs.Shape[rhs.NDim - i - 1];
+            }
+            res.NDim = dim;
+            lhs.BroadcastInplace(res);
+            rhs.BroadcastInplace(res);
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Algebra/Matmul.cs b/csharp/Tensor.NET/Algebra/Matmul.cs
new file mode 100644
index 0000000..1b07ba5
--- /dev/null
+++ b/csharp/Tensor.NET/Algebra/Matmul.cs
@@ -0,0 +1,270 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    public static class MatmulExtension{
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<T> Matmul<T>(this Tensor<T> lhs, Tensor<T> rhs) where T : struct, IEquatable<T>, IConvertible{
+            return MatmulInternal<T, T, T>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<double> lhs, Tensor<int> rhs){
+            return MatmulInternal<double, int, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<double> lhs, Tensor<float> rhs){
+            return MatmulInternal<double, float, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<double> lhs, Tensor<long> rhs){
+            return MatmulInternal<double, long, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<double> lhs, Tensor<bool> rhs){
+            return MatmulInternal<double, bool, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<int> lhs, Tensor<double> rhs){
+            return MatmulInternal<int, double, double>(lhs, rhs);
+        }
+        public static Tensor<float> Matmul(this Tensor<int> lhs, Tensor<float> rhs){
+            return MatmulInternal<int, float, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Matmul(this Tensor<int> lhs, Tensor<long> rhs){
+            return MatmulInternal<int, long, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<int> Matmul(this Tensor<int> lhs, Tensor<bool> rhs){
+            return MatmulInternal<int, bool, int>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<long> lhs, Tensor<double> rhs){
+            return MatmulInternal<long, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Matmul(this Tensor<long> lhs, Tensor<float> rhs){
+            return MatmulInternal<long, float, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Matmul(this Tensor<long> lhs, Tensor<int> rhs){
+            return MatmulInternal<long, int, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Matmul(this Tensor<long> lhs, Tensor<bool> rhs){
+            return MatmulInternal<long, bool, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Matmul(this Tensor<float> lhs, Tensor<long> rhs){
+            return MatmulInternal<float, long, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Matmul(this Tensor<float> lhs, Tensor<int> rhs){
+            return MatmulInternal<float, int, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Matmul(this Tensor<float> lhs, Tensor<bool> rhs){
+            return MatmulInternal<float, bool, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<float> lhs, Tensor<double> rhs){
+            return MatmulInternal<float, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<double> Matmul(this Tensor<bool> lhs, Tensor<double> rhs){
+            return MatmulInternal<bool, double, double>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<float> Matmul(this Tensor<bool> lhs, Tensor<float> rhs){
+            return MatmulInternal<bool, float, float>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<long> Matmul(this Tensor<bool> lhs, Tensor<long> rhs){
+            return MatmulInternal<bool, long, long>(lhs, rhs);
+        }
+        /// <summary>
+        /// Get the matrix mutiply result of the two tensors. For the details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.matmul.html
+        /// </summary>
+        /// <param name="lhs"></param>
+        /// <param name="rhs"></param>
+        /// <returns></returns>
+        public static Tensor<int> Matmul(this Tensor<bool> lhs, Tensor<int> rhs){
+            return MatmulInternal<bool, int, int>(lhs, rhs);
+        }
+        private static Tensor<TC> MatmulInternal<TA, TB, TC>(Tensor<TA> lhs, Tensor<TB> rhs) 
+        where TA : struct, IEquatable<TA>, IConvertible
+        where TB : struct, IEquatable<TB>, IConvertible
+        where TC : struct, IEquatable<TC>, IConvertible
+        {
+            TensorLayout leftLayout = new TensorLayout(lhs.TLayout);
+            TensorLayout rightLayout = new TensorLayout(rhs.TLayout);
+            Tensor<TC> res = new Tensor<TC>(DeduceLayout(leftLayout, rightLayout));
+            res.TLayout.InitContiguousLayout();
+            IntPtr status = NativeExecutor.Execute(NativeApi.Matmul, lhs.TMemory, rhs.TMemory, res.TMemory, leftLayout, rightLayout, res.TLayout, IntPtr.Zero, Tensor<TC>.Provider);
+            NativeStatus.AssertOK(status);
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout lhs, TensorLayout rhs){
+            TensorLayout res = new TensorLayout();
+            res.DType = DefaultTypeDeduce.Deduce(lhs.DType, rhs.DType);
+            if (lhs.NDim <= 0 || rhs.NDim <= 0)
+                throw new MismatchedShapeException(lhs, rhs, "Matmul");
+            if (lhs.IsScalar() && rhs.IsScalar()) {
+                res.Shape[0] = 1;
+                res.NDim = 1;
+                return res;
+            }
+            if (lhs.NDim == 1 && rhs.NDim == 1) {
+                res.Shape[0] = lhs.Shape[0];
+                res.Shape[1] = rhs.Shape[0];
+                res.NDim = 2;
+                lhs.BroadcastInplace(new TensorShape(lhs.Shape[0], 1));
+                rhs.BroadcastInplace(new TensorShape(1, rhs.Shape[0]));
+                return res;
+            }
+            int dim = lhs.NDim > rhs.NDim ? lhs.NDim : rhs.NDim;
+            int min_dim = lhs.NDim < rhs.NDim ? lhs.NDim : rhs.NDim;
+            res.NDim = dim;
+            TensorShape aDstShape = new TensorShape(lhs);
+            TensorShape bDstShape = new TensorShape(rhs);
+
+            for (int i = 0; i < dim - 2; i++) {
+                int aIdx = lhs.NDim - i - 3;
+                int bIdx = rhs.NDim - i - 3;
+                int targetIdx = dim - i - 3;
+                if (aIdx >= 0 &&
+                    (bIdx < 0 || lhs.Shape[aIdx] == 1 || rhs.Shape[bIdx] == 1 ||
+                    lhs.Shape[aIdx] == rhs.Shape[bIdx])) {
+                    if (bIdx < 0)
+                        res.Shape[targetIdx] = lhs.Shape[aIdx];
+                    else
+                        res.Shape[targetIdx] =
+                            lhs.Shape[aIdx] == 1 ? System.Math.Max(rhs.Shape[bIdx], lhs.Shape[aIdx]) : lhs.Shape[aIdx];
+                } else if (aIdx < 0) {
+                    res.Shape[targetIdx] = rhs.Shape[bIdx];
+                } else {
+                    throw new MismatchedShapeException(lhs, rhs, "Matmul");
+                }
+                aDstShape.Shape[targetIdx] = res.Shape[targetIdx];
+                bDstShape.Shape[targetIdx] = res.Shape[targetIdx];
+            }
+            if (lhs.NDim == 1 && rhs.Shape[rhs.NDim - 2] != lhs.Shape[0] ||
+                rhs.NDim == 1 && rhs.Shape[0] != lhs.Shape[lhs.NDim - 1] ||
+                lhs.NDim != 1 && rhs.NDim != 1 &&
+                    lhs.Shape[lhs.NDim - 1] != rhs.Shape[rhs.NDim - 2]) {
+                throw new MismatchedShapeException(lhs, rhs, "Matmul");
+            }
+            res.Shape[dim - 2] = aDstShape.Shape[dim - 2] =
+                lhs.NDim == 1 ? 1 : lhs.Shape[lhs.NDim - 2];
+            res.Shape[dim - 1] = bDstShape.Shape[dim - 1] =
+                rhs.NDim == 1 ? 1 : rhs.Shape[rhs.NDim - 1];
+            aDstShape.Shape[dim - 1] = bDstShape.Shape[dim - 2] = lhs.Shape[lhs.NDim - 1];
+            if (rhs.NDim == 1) {
+                rhs.Shape[1] = 1;
+                rhs.NDim = 2;
+                rhs.Stride[0] = 1;
+                rhs.Stride[1] = 0;
+            }
+            aDstShape.NDim = bDstShape.NDim = res.NDim;
+            lhs.BroadcastInplace(aDstShape);
+            rhs.BroadcastInplace(bDstShape);
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Algebra/MatrixInverse.cs b/csharp/Tensor.NET/Algebra/MatrixInverse.cs
new file mode 100644
index 0000000..32535ac
--- /dev/null
+++ b/csharp/Tensor.NET/Algebra/MatrixInverse.cs
@@ -0,0 +1,86 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class InverseExtension{
+        /// <summary>
+        /// Get the inverse of the tensor. For the details of the manipulation, please refer to https://numpy.org/doc/stable/reference/generated/numpy.linalg.inv.html
+        /// </summary>
+        /// <param name="src"></param>
+        /// <returns></returns>
+        public static Tensor<double> Inverse(this Tensor<int> src)
+        {
+            Tensor<double> res = new Tensor<double>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            InverseInternal<double>(src.ToTensor<double>(), res);
+            return res;
+        }
+        /// <summary>
+        /// Get the inverse of the tensor. For the details of the manipulation, please refer to https://numpy.org/doc/stable/reference/generated/numpy.linalg.inv.html
+        /// </summary>
+        /// <param name="src"></param>
+        /// <returns></returns>
+        public static Tensor<double> Inverse(this Tensor<long> src)
+        {
+            Tensor<double> res = new Tensor<double>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            InverseInternal<double>(src.ToTensor<double>(), res);
+            return res;
+        }
+        /// <summary>
+        /// Get the inverse of the tensor. For the details of the manipulation, please refer to https://numpy.org/doc/stable/reference/generated/numpy.linalg.inv.html
+        /// </summary>
+        /// <param name="src"></param>
+        /// <returns></returns>
+        public static Tensor<double> Inverse(this Tensor<double> src)
+        {
+            Tensor<double> res = new Tensor<double>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            if(src.TLayout.IsContiguous()){
+                InverseInternal<double>(src, res);
+            }
+            else{
+                InverseInternal<double>(src.ToContiguousTensor(), res);
+            }
+            return res;
+        }
+        /// <summary>
+        /// Get the inverse of the tensor. For the details of the manipulation, please refer to https://numpy.org/doc/stable/reference/generated/numpy.linalg.inv.html
+        /// </summary>
+        /// <param name="src"></param>
+        /// <returns></returns>
+        public static Tensor<float> Inverse(this Tensor<float> src)
+        {
+            Tensor<float> res = new Tensor<float>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            if(src.TLayout.IsContiguous()){
+                InverseInternal<float>(src, res);
+            }
+            else{
+                InverseInternal<float>(src.ToContiguousTensor(), res);
+            }
+            return res;
+        }
+        private unsafe static void InverseInternal<T>(Tensor<T> src, Tensor<T> dst) where T : struct, IEquatable<T>, IConvertible{
+            IntPtr status = NativeExecutor.Execute(NativeApi.MatrixInverse, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, IntPtr.Zero, Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            TensorLayout res = new TensorLayout();
+            if (src.NDim < 2) {
+                throw new MismatchedShapeException("The tensor to calculate inverse must has at least two dims.");
+            }
+            if (src.Shape[src.NDim - 1] != src.Shape[src.NDim - 2]) {
+                throw new MismatchedShapeException("The tensor to calculate inverse must has its last two dims square.");
+            }
+            res.DType = (src.DType is DType.Float32 or DType.Float64) ? src.DType : DType.Float64;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                res.Shape[i] = src.Shape[i];
+            }
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Arange.cs b/csharp/Tensor.NET/Creation/Arange.cs
new file mode 100644
index 0000000..680bb4d
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Arange.cs
@@ -0,0 +1,60 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Return evenly spaced values within a given interval. Values are generated within the half-open interval [0, stop) with step 1.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.arange.html?highlight=arange#numpy.arange
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="stop"> The upper bound of the range of generation. </param>
+        /// <returns></returns>
+        public static Tensor<T> Arange<T>(double stop) where T : struct, IConvertible, IEquatable<T>{
+            int num = (int)System.Math.Ceiling(stop);
+            Tensor<T> res = new Tensor<T>(new TensorShape(num), TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+            ArangeInternal<T>(res, 0, stop, 1);
+            return res;
+        }
+        /// <summary>
+        /// Return evenly spaced values within a given interval. Values are generated within the half-open interval [start, stop) with step 1.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.arange.html?highlight=arange#numpy.arange
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="start"> The lower bound of the range of generation. </param>
+        /// <param name="stop"> The upper bound of the range of generation. </param>
+        /// <returns></returns>
+        public static Tensor<T> Arange<T>(double start, double stop) where T : struct, IConvertible, IEquatable<T>{
+            int num = (int)System.Math.Ceiling(stop - start);
+            Tensor<T> res = new Tensor<T>(new TensorShape(num), TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+            ArangeInternal<T>(res, start, stop, 1);
+            return res;
+        }
+        /// <summary>
+        /// Return evenly spaced values within a given interval. Values are generated within the half-open interval [start, stop) with the given step.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.arange.html?highlight=arange#numpy.arange
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="start"> The lower bound of the range of generation. </param>
+        /// <param name="stop"> The upper bound of the range of generation. </param>
+        /// <param name="step"> The step of the generation. </param>
+        /// <returns></returns>
+        public static Tensor<T> Arange<T>(double start, double stop, double step) where T : struct, IConvertible, IEquatable<T>{
+            if(step <= 0){
+                throw new InvalidParamException("The step of arrange should be a positive number.");
+            }
+            int num = (int)System.Math.Ceiling((stop - start) / step);
+            Tensor<T> res = new Tensor<T>(new TensorShape(num), TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+            ArangeInternal<T>(res, start, stop, step);
+            return res;
+        }
+
+        private static unsafe void ArangeInternal<T>(Tensor<T> t, double start, double stop, double step) where T : struct, IConvertible, IEquatable<T>{
+            ArangeParam param = new ArangeParam() { start = start, stop = stop, step = step};
+            IntPtr status = NativeExecutor.Execute(NativeApi.Arange, t.TMemory, t.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Eye.cs b/csharp/Tensor.NET/Creation/Eye.cs
new file mode 100644
index 0000000..346c3d9
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Eye.cs
@@ -0,0 +1,28 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Return a 2-D tensor with ones on the diagonal and zeros elsewhere.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.eye.html?highlight=eye#numpy.eye
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="rows"> Number of rows in the output. </param>
+        /// <param name="cols"> Number of columns in the output. </param>
+        /// <param name="k"> Index of the diagonal: 0 (the default) refers to the main diagonal, a positive value refers to an upper diagonal, and a negative value to a lower diagonal. </param>
+        /// <returns></returns>
+        public static Tensor<T> Eye<T>(int rows, int cols, int k) where T : struct, IConvertible, IEquatable<T>{
+            Tensor<T> res = new Tensor<T>(new TensorShape(rows, cols), TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+            EyeInternal<T>(res, k);
+            return res;
+        }
+
+        private static unsafe void EyeInternal<T>(Tensor<T> t, int k) where T : struct, IConvertible, IEquatable<T>{
+            EyeParam param = new EyeParam() { k = k };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Eye, t.TMemory, t.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Identity.cs b/csharp/Tensor.NET/Creation/Identity.cs
new file mode 100644
index 0000000..395aaa9
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Identity.cs
@@ -0,0 +1,18 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Return the identity tensor, which is a square two-dimensional tensor with ones on the main diagonal.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.identity.html#numpy.identity
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="n"> Number of rows (and columns). </param>
+        /// <returns></returns>
+        public static Tensor<T> Idendity<T>(int n) where T : struct, IConvertible, IEquatable<T>{
+            return Tensor.Eye<T>(n, n, 0);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Linspace.cs b/csharp/Tensor.NET/Creation/Linspace.cs
new file mode 100644
index 0000000..21da7d4
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Linspace.cs
@@ -0,0 +1,29 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Returns num evenly spaced samples, calculated over the interval [start, stop]. The endpoint of the interval can optionally be excluded.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.linspace.html?highlight=linspace#numpy.linspace
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="start"> The starting value of the sequence. </param>
+        /// <param name="stop"> The end value of the sequence, unless endpoint is set to False. In that case, the sequence consists of all but the last of num + 1 evenly spaced samples, so that stop is excluded. Note that the step size changes when endpoint is False. </param>
+        /// <param name="num"> Number of samples to generate, which must be non-negative. </param>
+        /// <param name="isEndPoint"> If true, stop is the last sample. Otherwise, it is not included. Default is true. </param>
+        /// <returns></returns>
+        public static Tensor<T> Linspace<T>(double start, double stop, int num, bool isEndPoint = true) where T : struct, IConvertible, IEquatable<T>{
+            Tensor<T> res = new Tensor<T>(new TensorShape(num), TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+            LinspaceInternal<T>(res, start, stop, num, isEndPoint);
+            return res;
+        }
+
+        private static unsafe void LinspaceInternal<T>(Tensor<T> t, double start, double stop, int num, bool isEndPoint) where T : struct, IConvertible, IEquatable<T>{
+            LinspaceParam param = new LinspaceParam() { start = start, stop = stop, num = num, isEndpoint = isEndPoint };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Linspace, t.TMemory, t.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Onehot.cs b/csharp/Tensor.NET/Creation/Onehot.cs
new file mode 100644
index 0000000..45248ed
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Onehot.cs
@@ -0,0 +1,38 @@
+using Tensornet.Native.Param;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Get the onehot data of a tensor on the last axis. The tensor shape depends on the maxValue parameter.
+        /// For example, tensor [1, 2] with maxValue = 3 will create an onehot tensor
+        /// [[0, 1, 0, 0],
+        ///  [0, 0, 1, 0]]
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="maxValue"> The max value of the onehot. </param>
+        /// <returns></returns>
+        public static Tensor<T> Onehot<T>(Tensor<T> src, int maxValue) where T : struct, IConvertible, IEquatable<T>{
+            Tensor<T> res = new Tensor<T>(DeduceOnehotLayout(src.TLayout, maxValue));
+            res.TLayout.InitContiguousLayout();
+            OnehotInternal(src, res, maxValue);
+            return res;
+        }
+        private unsafe static void OnehotInternal<T>(Tensor<T> src, Tensor<T> dst, int maxValue) where T : struct, IEquatable<T>, IConvertible{
+            OnehotParam p = new OnehotParam() { maxValue = maxValue };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Onehot, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceOnehotLayout(TensorLayout layout, int maxValue){
+            TensorLayout res = new TensorLayout();
+            res.DType = layout.DType;
+            res.NDim = layout.NDim + 1;
+            for (int i = 0; i < layout.NDim; i++) {
+                res.Shape[i] = layout.Shape[i];
+            }
+            res.Shape[res.NDim - 1] = maxValue + 1;
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Ones.cs b/csharp/Tensor.NET/Creation/Ones.cs
new file mode 100644
index 0000000..92e46f0
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Ones.cs
@@ -0,0 +1,38 @@
+using Tensornet.Common;
+using Tensornet.Native.Param;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Return a new tensor of given shape and type, filled with ones.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.ones.html?highlight=ones#numpy.ones
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="shape"> Shape of the new tensor. </param>
+        /// <returns></returns>
+        public static Tensor<T> Ones<T>(TensorShape shape) where T : struct, IConvertible, IEquatable<T>{
+            Tensor<T> res = new Tensor<T>(new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
+            OnesInternal(res);
+            return res;
+        }
+        private unsafe static void OnesInternal<T>(Tensor<T> src) where T : struct, IEquatable<T>, IConvertible{
+            FillParam param = new FillParam() { value = 1.0 };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Fill, src.TMemory, src.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        /// <summary>
+        /// Return an array of ones with the same shape and type as a given array.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.ones_like.html#numpy.ones_like
+        /// </summary>
+        /// <typeparam name="TResult"></typeparam>
+        /// <typeparam name="TRefer"></typeparam>
+        /// <param name="tensor"> The tensor with target shape. </param>
+        /// <returns></returns>
+        public static Tensor<TResult> OnesLike<TResult, TRefer>(Tensor<TRefer> tensor) 
+            where TResult : struct, IConvertible, IEquatable<TResult> 
+            where TRefer : struct, IConvertible, IEquatable<TRefer>{
+            return Tensor.Ones<TResult>(tensor.TLayout);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Creation/Zeros.cs b/csharp/Tensor.NET/Creation/Zeros.cs
new file mode 100644
index 0000000..74b03b9
--- /dev/null
+++ b/csharp/Tensor.NET/Creation/Zeros.cs
@@ -0,0 +1,39 @@
+using Tensornet.Common;
+using Tensornet.Native.Param;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Return a new tensor of given shape and type, filled with zeros.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.zeros.html#numpy.zeros
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="shape"> Shape of the new tensor. </param>
+        /// <returns></returns>
+        public static Tensor<T> Zeros<T>(TensorShape shape) where T : struct, IConvertible, IEquatable<T>{
+            Tensor<T> res = new Tensor<T>(new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
+            ZerosInternal(res);
+            return res;
+        }
+        private unsafe static void ZerosInternal<T>(Tensor<T> src) where T : struct, IEquatable<T>, IConvertible{
+            FillParam param = new FillParam() { value = .0 };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Fill, src.TMemory, src.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        /// <summary>
+        /// Return an array of zeros with the same shape and type as a given array.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.zeros_like.html#numpy.zeros_like
+        /// </summary>
+        /// <typeparam name="TResult"></typeparam>
+        /// <typeparam name="TRefer"></typeparam>
+        /// <param name="tensor"> The tensor with target shape. </param>
+        /// <returns></returns>
+
+        public static Tensor<TResult> ZerosLike<TResult, TRefer>(Tensor<TRefer> tensor) 
+            where TResult : struct, IConvertible, IEquatable<TResult> 
+            where TRefer : struct, IConvertible, IEquatable<TRefer>{
+            return Tensor.Zeros<TResult>(tensor.TLayout);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Exceptions/InvalidArgumentException.cs b/csharp/Tensor.NET/Exceptions/InvalidArgumentException.cs
similarity index 88%
rename from csharp/Num.NET/Exceptions/InvalidArgumentException.cs
rename to csharp/Tensor.NET/Exceptions/InvalidArgumentException.cs
index 4ee17bc..f6247b5 100644
--- a/csharp/Num.NET/Exceptions/InvalidArgumentException.cs
+++ b/csharp/Tensor.NET/Exceptions/InvalidArgumentException.cs
@@ -1,4 +1,4 @@
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class InvalidArgumentException:Exception{
         protected string _message;
         public override string Message => _message;
diff --git a/csharp/Num.NET/Exceptions/InvalidParamException.cs b/csharp/Tensor.NET/Exceptions/InvalidParamException.cs
similarity index 90%
rename from csharp/Num.NET/Exceptions/InvalidParamException.cs
rename to csharp/Tensor.NET/Exceptions/InvalidParamException.cs
index 9c1fddc..e562531 100644
--- a/csharp/Num.NET/Exceptions/InvalidParamException.cs
+++ b/csharp/Tensor.NET/Exceptions/InvalidParamException.cs
@@ -1,4 +1,4 @@
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class InvalidParamException:Exception{
         protected string _message;
         public override string Message => _message;
diff --git a/csharp/Num.NET/Exceptions/InvalidShapeException.cs b/csharp/Tensor.NET/Exceptions/InvalidShapeException.cs
similarity index 80%
rename from csharp/Num.NET/Exceptions/InvalidShapeException.cs
rename to csharp/Tensor.NET/Exceptions/InvalidShapeException.cs
index 1900768..2654ab0 100644
--- a/csharp/Num.NET/Exceptions/InvalidShapeException.cs
+++ b/csharp/Tensor.NET/Exceptions/InvalidShapeException.cs
@@ -1,4 +1,4 @@
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class InvalidShapeException:Exception{
         protected string _message;
         public override string Message => _message;
@@ -13,7 +13,7 @@ public class DimExceedException:Exception{
         protected string _message;
         public override string Message => _message;
         public DimExceedException(int dim){
-            _message = $"Exceed max dim: the max is {Numnet.Common.TensorShape.MAX_NDIM}, but {dim} is rerquired.";
+            _message = $"Exceed max dim: the max is {TensorShape.MAX_NDIM}, but {dim} is rerquired.";
         }
     }
 }
\ No newline at end of file
diff --git a/csharp/Num.NET/Exceptions/MismatchedShapeException.cs b/csharp/Tensor.NET/Exceptions/MismatchedShapeException.cs
similarity index 89%
rename from csharp/Num.NET/Exceptions/MismatchedShapeException.cs
rename to csharp/Tensor.NET/Exceptions/MismatchedShapeException.cs
index d7203f4..9509d5a 100644
--- a/csharp/Num.NET/Exceptions/MismatchedShapeException.cs
+++ b/csharp/Tensor.NET/Exceptions/MismatchedShapeException.cs
@@ -1,6 +1,6 @@
-using Numnet.Common;
+using Tensornet.Common;
 
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class MismatchedShapeException:Exception{
         protected string _message;
         public override string Message => _message;
diff --git a/csharp/Num.NET/Exceptions/MismatchedTypeException.cs b/csharp/Tensor.NET/Exceptions/MismatchedTypeException.cs
similarity index 90%
rename from csharp/Num.NET/Exceptions/MismatchedTypeException.cs
rename to csharp/Tensor.NET/Exceptions/MismatchedTypeException.cs
index 39925cf..88c6148 100644
--- a/csharp/Num.NET/Exceptions/MismatchedTypeException.cs
+++ b/csharp/Tensor.NET/Exceptions/MismatchedTypeException.cs
@@ -1,4 +1,4 @@
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class MismatchedTypeException:Exception{
         protected string _message;
         public override string Message => _message;
diff --git a/csharp/Num.NET/Exceptions/NNRuntimeException.cs b/csharp/Tensor.NET/Exceptions/NNRuntimeException.cs
similarity index 80%
rename from csharp/Num.NET/Exceptions/NNRuntimeException.cs
rename to csharp/Tensor.NET/Exceptions/NNRuntimeException.cs
index 5744105..5ea4ca0 100644
--- a/csharp/Num.NET/Exceptions/NNRuntimeException.cs
+++ b/csharp/Tensor.NET/Exceptions/NNRuntimeException.cs
@@ -1,6 +1,6 @@
-using Numnet.Native;
+using Tensornet.Native;
 
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class NNRuntimeException:Exception{
         protected string _message;
         public override string Message => _message;
diff --git a/csharp/Tensor.NET/Exceptions/NNSerializeException.cs b/csharp/Tensor.NET/Exceptions/NNSerializeException.cs
new file mode 100644
index 0000000..5216cfa
--- /dev/null
+++ b/csharp/Tensor.NET/Exceptions/NNSerializeException.cs
@@ -0,0 +1,11 @@
+using Tensornet.Native;
+
+namespace Tensornet.Exceptions{
+    public class NNSerializeException:Exception{
+        protected string _message;
+        public override string Message => _message;
+        public NNSerializeException(string message){
+            _message = message;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Exceptions/NativeSystemException.cs b/csharp/Tensor.NET/Exceptions/NativeSystemException.cs
similarity index 100%
rename from csharp/Num.NET/Exceptions/NativeSystemException.cs
rename to csharp/Tensor.NET/Exceptions/NativeSystemException.cs
diff --git a/csharp/Num.NET/Exceptions/UnsupportedTypeException.cs b/csharp/Tensor.NET/Exceptions/UnsupportedTypeException.cs
similarity index 91%
rename from csharp/Num.NET/Exceptions/UnsupportedTypeException.cs
rename to csharp/Tensor.NET/Exceptions/UnsupportedTypeException.cs
index f883610..b021576 100644
--- a/csharp/Num.NET/Exceptions/UnsupportedTypeException.cs
+++ b/csharp/Tensor.NET/Exceptions/UnsupportedTypeException.cs
@@ -1,4 +1,4 @@
-namespace Numnet.Exceptions{
+namespace Tensornet.Exceptions{
     public class UnsupportedTypeException:Exception{
         protected string _message;
         public override string Message => _message;
diff --git a/csharp/Tensor.NET/Manipulation/Concat.cs b/csharp/Tensor.NET/Manipulation/Concat.cs
new file mode 100644
index 0000000..73db172
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Concat.cs
@@ -0,0 +1,104 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+using System.Buffers;
+using System.Runtime.InteropServices;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        /// <summary>
+        /// Join a sequence of arrays along an existing axis.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.concatenate.html?highlight=concat#numpy.concatenate
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis along which the arrays will be joined. </param>
+        /// <returns></returns>
+        public static Tensor<T> Concat<T>(IEnumerable<Tensor<T>> src, int axis) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(ConcatDeduceLayout(src, axis));
+            res.TLayout.InitContiguousLayout();
+            ConcatInternal(src.Select(x => x.TMemory).ToArray(), src.Select(x => x.TLayout).ToArray(), res, axis);
+            return res;
+        }
+        private unsafe static void ConcatInternal<T>(TensorMemory<T>[] srcMemory, TensorLayout[] srcLayout, Tensor<T> dst, int axis) where T : struct, IEquatable<T>, IConvertible{
+            MemoryHandle[] srcHandles = new MemoryHandle[srcMemory.Length];
+            MemoryHandle dstHandle;
+            dst.TMemory.Pin(out dstHandle);
+            IntPtr[] srcPtrs = srcHandles.Select(x => new IntPtr(x.Pointer)).ToArray();
+            GCHandle[] shapeHandles = new GCHandle[srcMemory.Length];
+            GCHandle[] strideHandles = new GCHandle[srcMemory.Length];
+            NativeTensor[] nativeTensors = new NativeTensor[srcMemory.Length];
+            for (int i = 0; i < srcMemory.Length; i++){
+                shapeHandles[i] = GCHandle.Alloc(srcLayout[i].Shape, GCHandleType.Pinned);
+                strideHandles[i] = GCHandle.Alloc(srcLayout[i].Stride, GCHandleType.Pinned);
+                srcMemory[i].Pin(out srcHandles[i]);
+                nativeTensors[i] = new NativeTensor()
+                {
+                    dtype = srcLayout[i].DType,
+                    ndim = srcLayout[i].NDim,
+                    offset = srcLayout[i].Offset * TensorTypeInfo.GetTypeSize(srcLayout[i].DType),
+                    shape = shapeHandles[i].AddrOfPinnedObject(),
+                    stride = strideHandles[i].AddrOfPinnedObject(),
+                    data = new IntPtr(srcHandles[i].Pointer)
+                };
+            }
+            IntPtr status;
+            ConcatParam param = new ConcatParam() { axis = axis };
+            fixed (int* shapeOupPtr = dst.TLayout.Shape, strideOupPtr = dst.TLayout.Stride){
+                NativeTensor nativeOup = new NativeTensor(){
+                    dtype = dst.TLayout.DType,
+                    ndim = dst.TLayout.NDim,
+                    offset = dst.TLayout.Offset * TensorTypeInfo.GetTypeSize(dst.TLayout.DType),
+                    shape = new IntPtr(shapeOupPtr),
+                    stride = new IntPtr(strideOupPtr),
+                    data = new IntPtr(dstHandle.Pointer)
+                };
+                fixed(NativeTensor* nativeTensorsPtr = nativeTensors){
+                    status = NativeApi.Concat(new IntPtr(nativeTensorsPtr), nativeTensors.Length, new IntPtr(&nativeOup), new IntPtr(&param), Tensor<T>.Provider);
+                }
+            }
+            NativeStatus.AssertOK(status);
+
+            dstHandle.Dispose();
+            for (int i = 0; i < shapeHandles.Length; i++){
+                shapeHandles[i].Free();
+                strideHandles[i].Free();
+            }
+        }
+        private static TensorLayout ConcatDeduceLayout<T>(IEnumerable<Tensor<T>> src, int axis) where T : struct, IEquatable<T>, IConvertible{
+            TensorLayout res = new TensorLayout();
+            int n = src.Count();
+            if (n < 2) {
+                throw new InvalidArgumentException("At least two tensor should be srcutted to execute the concat op.");
+            }
+            var enumerator = src.GetEnumerator();
+            enumerator.MoveNext();
+            int ndim = enumerator.Current.TLayout.NDim;
+            int sizeOnAxis = enumerator.Current.TLayout.Shape[axis];
+            res.DType = enumerator.Current.TLayout.DType;
+            for (int i = 0; i < ndim; i++) {
+                res.Shape[i] = enumerator.Current.TLayout.Shape[i];
+            }
+            while(enumerator.MoveNext()) {
+                if (enumerator.Current.TLayout.NDim != ndim) {
+                    throw new InvalidArgumentException("The tensors to concat must have the same ndim.");
+                }
+                for (int j = 0; j < ndim; j++) {
+                    if (j != axis &&
+                        enumerator.Current.TLayout.Shape[j] != enumerator.Current.TLayout.Shape[j]) {
+                        throw new InvalidArgumentException("Tensors to concat should have the same shape except the specified axis.");
+                    }
+                }
+                sizeOnAxis += enumerator.Current.TLayout.Shape[axis];
+            }
+            if (axis < 0 || axis >= ndim) {
+                throw new InvalidParamException("The specified axis to concat exceeds the max ndim of the tensors.");
+            }
+            res.NDim = ndim;
+            res.Shape[axis] = sizeOnAxis;
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Flatten.cs b/csharp/Tensor.NET/Manipulation/Flatten.cs
new file mode 100644
index 0000000..6f8c21c
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Flatten.cs
@@ -0,0 +1,27 @@
+namespace Tensornet{
+    public static class FlattenExtension{
+        /// <summary>
+        /// Flatten the tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <returns></returns>
+        public static Tensor<T> Flatten<T>(this Tensor<T> src) where T : struct, IEquatable<T>, IConvertible
+        {
+            return src.Reshape(new int[] { src.TLayout.TotalElemCount() });
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Flatten the tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <returns></returns>
+        public static Tensor<T> Flatten<T>(Tensor<T> src) where T : struct, IEquatable<T>, IConvertible
+        {
+            return src.Reshape(new int[] { src.TLayout.TotalElemCount() });
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Flip.cs b/csharp/Tensor.NET/Manipulation/Flip.cs
new file mode 100644
index 0000000..b90bd73
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Flip.cs
@@ -0,0 +1,91 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class FlipExtension{
+        /// <summary>
+        /// Reverse the order of elements in an array along the given axis. The shape of the array is preserved, but the elements are reordered.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.flip.html?highlight=flip#numpy.flip
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axes"> Axes along which to flip over. If no axis is given, then the tensor will be flipped on all axes.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        public static Tensor<T> Flip<T>(this Tensor<T> src, params int[] axes) where T : struct, IEquatable<T>, IConvertible
+        {
+            bool[] dims = new bool[TensorLayout.MAX_NDIM];
+            if(axes.Length == 0){
+                for (int i = 0; i < src.TLayout.NDim; i++){
+                    dims[i] = true;
+                }
+            }
+            else{
+                for (int i = 0; i < dims.Length; i++){
+                    dims[i] = false;
+                }
+                foreach (var i in axes)
+                {
+                    if (i >= src.TLayout.NDim)
+                    {
+                        throw new InvalidParamException("The axis to flip exceeds the max dim of the tensor.");
+                    }
+                    else if (i < 0)
+                    {
+                        throw new InvalidParamException("The axis to flip must be a positive number");
+                    }
+                    else
+                    {
+                        dims[i] = true;
+                    }
+                }
+            }
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            FlipInternal(src, res, dims);
+            return res;
+        }
+        private unsafe static void FlipInternal<T>(Tensor<T> src, Tensor<T> dst, bool[] dims) where T : struct, IEquatable<T>, IConvertible{
+            fixed(bool* pdims = dims){
+                FlipParam p = new FlipParam() { dims = new IntPtr(pdims) };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Flip, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            return new TensorLayout(src, true);
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Reverse the order of elements in an array along the given axis. The shape of the array is preserved, but the elements are reordered.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.flip.html?highlight=flip#numpy.flip
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src">the tensor to be flipped</param>
+        /// <param name="axes"> The axes to flip. If it's set to null, then all axis will be flipped. </param>
+        /// <returns>The flipped tensor</returns>
+        public static Tensor<T> Flip<T>(Tensor<T> src, int[]? axes = null) where T : struct, IEquatable<T>, IConvertible{
+            if(axes is null){
+                return src.Flip();
+            }
+            else{
+                return src.Flip(axes);
+            }
+        }
+        /// <summary>
+        /// Reverse the order of elements in an array along the given axis. The shape of the array is preserved, but the elements are reordered.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.flip.html?highlight=flip#numpy.flip
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src">the tensor to be flipped</param>
+        /// <param name="axis"> The axis to flip. </param>
+        /// <returns>The flipped tensor</returns>
+        public static Tensor<T> Flip<T>(Tensor<T> src, int axis) where T : struct, IEquatable<T>, IConvertible{
+            return src.Flip(axis);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Pad.cs b/csharp/Tensor.NET/Manipulation/Pad.cs
new file mode 100644
index 0000000..a784064
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Pad.cs
@@ -0,0 +1,229 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public enum PadMode : Int32{
+        Constant = 1,
+        Edge = 2,
+        Maximum = 3,
+        Minimum = 4,
+        Medium = 5,
+        Mean = 6
+    }
+
+    public static class PadExtension{
+        /// <summary>
+        /// Pad a tensor with given values.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.pad.html?highlight=pad#numpy.pad
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="widths"> Number of values padded to the edges of each axis. ((before_1, after_1), … (before_N, after_N)) unique pad widths for each axis. </param>
+        /// <param name="mode"> The mode of the padding.</param>
+        /// <param name="constants"> Used in ‘constant’. The values to set the padded values for each axis. ((before_1, after_1), ... (before_N, after_N)) unique pad constants for each axis. Default is 0.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        /// <exception cref="NotImplementedException"></exception>
+        public static Tensor<T> Pad<T>(this Tensor<T> src, (int, int)[] widths, PadMode mode = PadMode.Constant, 
+            (double, double)[]? constants = null) where T : struct, IEquatable<T>, IConvertible
+        {
+            if(widths.Length != src.TLayout.NDim){
+                throw new InvalidParamException($"The tensor to pad has {src.TLayout.NDim} dims, but only {widths.Length} dims of width are specified.");
+            }
+            if(mode == PadMode.Medium || mode == PadMode.Mean){
+                throw new NotImplementedException("This padding mode has not been implemented yet. It will be implemented in the future version.");
+            }
+            int[] paramWidths = new int[widths.Length * 2];
+            for (int i = 0; i < widths.Length; i++){
+                paramWidths[i * 2] = widths[i].Item1;
+                paramWidths[i * 2 + 1] = widths[i].Item2;
+            }
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, paramWidths));
+            res.TLayout.InitContiguousLayout();
+            if (mode == PadMode.Constant)
+            {
+                double[] paramConstants = new double[paramWidths.Length];
+                if(constants is null){
+                    paramConstants.AsSpan().Fill(0);
+                }
+                else{
+                    if(constants.Length != widths.Length){
+                        throw new InvalidParamException($"{widths.Length} pairs constant should be specified, but only get {constants.Length} pairs.");
+                    }
+                    for (int i = 0; i < constants.Length; i++){
+                        paramConstants[i * 2] = constants[i].Item1;
+                        paramConstants[i * 2 + 1] = constants[i].Item2;
+                    }
+                }
+                PadInternal(src, res, mode, paramWidths, paramConstants);
+            }
+            else{
+                PadInternal(src, res, mode, paramWidths, null);
+            }
+            return res;
+        }
+        /// <summary>
+        /// Pad a tensor with given values.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.pad.html?highlight=pad#numpy.pad
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="widths"> Number of values padded to the edges of each axis. ((width_1, width_1), … (width_N, width_N)) same pad widths for each axis. </param>
+        /// <param name="mode"> The mode of the padding.</param>
+        /// <param name="constants"> Used in ‘constant’. The values to set the padded values for each axis. ((value_1, value_1), ... (value_N, value_N)) same pad constants for each axis. Default is 0.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        /// <exception cref="NotImplementedException"></exception>
+        public static Tensor<T> Pad<T>(this Tensor<T> src, int[] widths, PadMode mode = PadMode.Constant, 
+            double[]? constants = null) where T : struct, IEquatable<T>, IConvertible
+        {
+            if(widths.Length != src.TLayout.NDim){
+                throw new InvalidParamException($"The tensor to pad has {src.TLayout.NDim} dims, but only {widths.Length} dims of width are specified.");
+            }
+            if(mode == PadMode.Medium || mode == PadMode.Mean){
+                throw new NotImplementedException("This padding mode has not been implemented yet. It will be implemented in the future version.");
+            }
+            int[] paramWidths = new int[widths.Length * 2];
+            for (int i = 0; i < widths.Length; i++){
+                paramWidths[i * 2] = paramWidths[i * 2 + 1] = widths[i];
+            }
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, paramWidths));
+            res.TLayout.InitContiguousLayout();
+            if (mode == PadMode.Constant)
+            {
+                double[] paramConstants = new double[paramWidths.Length];
+                if(constants is null){
+                    paramConstants.AsSpan().Fill(0);
+                }
+                else{
+                    if(constants.Length != widths.Length){
+                        throw new InvalidParamException($"{widths.Length} pairs constant should be specified, but only get {constants.Length} pairs.");
+                    }
+                    for (int i = 0; i < constants.Length; i++){
+                        paramConstants[i * 2] = paramConstants[i * 2 + 1] = constants[i];
+                    }
+                }
+                PadInternal(src, res, mode, paramWidths, paramConstants);
+            }
+            else{
+                PadInternal(src, res, mode, paramWidths, null);
+            }
+            return res;
+        }
+        /// <summary>
+        /// Pad a tensor with given values.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.pad.html?highlight=pad#numpy.pad
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="width"> Number of values padded to the edges of each axis. ((width_1, width_1), … (width_N, width_N)) same pad widths for each axis. </param>
+        /// <param name="mode"> The mode of the padding.</param>
+        /// <param name="constant"> Used in ‘constant’. The values to set the padded values for each axis. ((value_1, value_1), ... (value_N, value_N)) same pad constants for each axis. Default is 0.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        /// <exception cref="NotImplementedException"></exception>
+        public static Tensor<T> Pad<T>(this Tensor<T> src, int width, PadMode mode = PadMode.Constant, 
+            double constant = 0) where T : struct, IEquatable<T>, IConvertible
+        {
+            if(mode == PadMode.Medium || mode == PadMode.Mean){
+                throw new NotImplementedException("This padding mode has not been implemented yet. It will be implemented in the future version.");
+            }
+            int[] paramWidths = new int[src.TLayout.NDim * 2];
+            for (int i = 0; i < src.TLayout.NDim; i++){
+                paramWidths[i * 2] = paramWidths[i * 2 + 1] = width;
+            }
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, paramWidths));
+            res.TLayout.InitContiguousLayout();
+            if (mode == PadMode.Constant)
+            {
+                double[] paramConstants = new double[paramWidths.Length];
+                paramConstants.AsSpan().Fill(constant);
+                PadInternal(src, res, mode, paramWidths, paramConstants);
+            }
+            else{
+                PadInternal(src, res, mode, paramWidths, null);
+            }
+            return res;
+        }
+        private unsafe static void PadInternal<T>(Tensor<T> src, Tensor<T> dst, PadMode mode, int[] widths, double[]? constants) where T : struct, IEquatable<T>, IConvertible{
+            fixed(int* pwidths = widths){
+                if(constants is null){
+                    PadParam p = new PadParam() { mode = mode, size = widths.Length, widths = new IntPtr(pwidths), constants = IntPtr.Zero };
+                    IntPtr status = NativeExecutor.Execute(NativeApi.Pad, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                    NativeStatus.AssertOK(status);
+                }
+                else{
+                    fixed(double* pconstants = constants){
+                        PadParam p = new PadParam() { mode = mode, size = widths.Length, widths = new IntPtr(pwidths), constants = new IntPtr(pconstants) };
+                        IntPtr status = NativeExecutor.Execute(NativeApi.Pad, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                        NativeStatus.AssertOK(status);
+                    }
+                }
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int[] widths){
+            TensorLayout res = new TensorLayout();
+            res.DType = src.DType;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                if (widths[i * 2] < 0 || widths[i * 2 + 1] < 0) {
+                    throw new InvalidParamException("Padding width must be a positive number");
+                }
+                res.Shape[i] = src.Shape[i] + widths[i * 2] + widths[i * 2 + 1];
+            }
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Pad a tensor with given values.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.pad.html?highlight=pad#numpy.pad
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The teneosr to be padded. </param>
+        /// <param name="widths"> Number of values padded to the edges of each axis. ((before_1, after_1), … (before_N, after_N)) unique pad widths for each axis. </param>
+        /// <param name="mode"> The mode of the padding.</param>
+        /// <param name="constants"> Used in ‘constant’. The values to set the padded values for each axis. ((before_1, after_1), ... (before_N, after_N)) unique pad constants for each axis. Default is 0.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        /// <exception cref="NotImplementedException"></exception>
+        public static Tensor<T> Pad<T>(Tensor<T> src, (int, int)[] widths, PadMode mode = PadMode.Constant, 
+            (double, double)[]? constants = null) where T : struct, IEquatable<T>, IConvertible{
+            return src.Pad(widths, mode, constants);
+        }
+        /// <summary>
+        /// Pad a tensor with given values.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.pad.html?highlight=pad#numpy.pad
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The teneosr to be padded. </param>
+        /// <param name="widths"> Number of values padded to the edges of each axis. ((width_1, width_1), … (width_N, width_N)) same pad widths for each axis. </param>
+        /// <param name="mode"> The mode of the padding.</param>
+        /// <param name="constants"> Used in ‘constant’. The values to set the padded values for each axis. ((value_1, value_1), ... (value_N, value_N)) same pad constants for each axis. Default is 0.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        /// <exception cref="NotImplementedException"></exception>
+        public static Tensor<T> Pad<T>(Tensor<T> src, int[] widths, PadMode mode = PadMode.Constant, 
+            double[]? constants = null) where T : struct, IEquatable<T>, IConvertible{
+            return src.Pad(widths, mode, constants);
+        }
+        /// <summary>
+        /// Pad a tensor with given values.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.pad.html?highlight=pad#numpy.pad
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The teneosr to be padded. </param>
+        /// <param name="widths"> Number of values padded to the edges of each axis. ((width_1, width_1), … (width_N, width_N)) same pad widths for each axis. </param>
+        /// <param name="mode"> The mode of the padding.</param>
+        /// <param name="constants"> Used in ‘constant’. The values to set the padded values for each axis. ((value_1, value_1), ... (value_N, value_N)) same pad constants for each axis. Default is 0.</param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        /// <exception cref="NotImplementedException"></exception>
+        public static Tensor<T> Pad<T>(Tensor<T> src, int widths, PadMode mode = PadMode.Constant, 
+            double constants = 0) where T : struct, IEquatable<T>, IConvertible{
+            return src.Pad(widths, mode, constants);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Permute.cs b/csharp/Tensor.NET/Manipulation/Permute.cs
new file mode 100644
index 0000000..9690af1
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Permute.cs
@@ -0,0 +1,48 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class PermuteExtension{
+        /// <summary>
+        /// Permute the axes of a tensor.
+        /// For example, the input is s tensor with shape [2, 3, 4], the parameter is [2, 0, 1], the shapee of the result is [4, 2, 3].
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="dims"> The new arrangement of the dims.</param>
+        /// <returns></returns>
+        public static Tensor<T> Permute<T>(this Tensor<T> src, params int[] dims) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, dims));
+            res.TLayout.InitContiguousLayout();
+            PermuteInternal(src, res, dims);
+            return res;
+        }
+        private unsafe static void PermuteInternal<T>(Tensor<T> src, Tensor<T> dst, int[] dims) where T : struct, IEquatable<T>, IConvertible{
+            fixed(int* pdims = dims){
+                PermuteParam p = new PermuteParam() { dims = new IntPtr(pdims) };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Permute, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int[] dims){
+            TensorLayout res = new TensorLayout();
+            int duplicated = 0;
+            res.DType = src.DType;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                if (dims[i] >= src.NDim) {
+                    throw new InvalidParamException("Invalid param for permute.");
+                }
+                res.Shape[i] = src.Shape[dims[i]];
+                if ((duplicated & (1 << dims[i])) != 0) {
+                    throw new InvalidParamException("Duplicated index in the param of permute.");
+                }
+                duplicated |= 1 << dims[i];
+            }
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Repeat.cs b/csharp/Tensor.NET/Manipulation/Repeat.cs
new file mode 100644
index 0000000..d420441
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Repeat.cs
@@ -0,0 +1,61 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class RepeatExtension{
+        /// <summary>
+        /// Repeat elements of a tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.repeat.html?highlight=repeat
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="repeats"> The number of repetitions for each element. repeats is broadcasted to fit the shape of the given axis. </param>
+        /// <param name="axis"> The axis along which to repeat values. </param>
+        /// <returns></returns>
+        public static Tensor<T> Repeat<T>(this Tensor<T> src, int repeats, int axis) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, repeats, axis));
+            res.TLayout.InitContiguousLayout();
+            RepeatInternal(src, res, repeats, axis);
+            return res;
+        }
+        private unsafe static void RepeatInternal<T>(Tensor<T> src, Tensor<T> dst, int repeats, int axis) where T : struct, IEquatable<T>, IConvertible{
+            RepeatParam param = new RepeatParam() { repeats = repeats, axis = axis };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Repeat, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int repeats, int axis){
+            TensorLayout res = new TensorLayout();
+            if(repeats <= 0){
+                throw new InvalidParamException("The repeats count must be a positive number.");
+            }
+            if (axis < 0 || axis >= src.NDim) {
+                throw new InvalidParamException("Invalid param for repeat.");
+            }
+            res.DType = src.DType;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                res.Shape[i] = src.Shape[i];
+            }
+            res.Shape[axis] = src.Shape[axis] * repeats;
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Repeat elements of a tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.repeat.html?highlight=repeat
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="repeats"> The number of repetitions for each element. repeats is broadcasted to fit the shape of the given axis. </param>
+        /// <param name="axis"> The axis along which to repeat values. </param>
+        /// <returns></returns>
+        public static Tensor<T> Repeat<T>(Tensor<T> src, int repeats, int axis) where T : struct, IEquatable<T>, IConvertible{
+            return src.Repeat(repeats, axis);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Rotate.cs b/csharp/Tensor.NET/Manipulation/Rotate.cs
new file mode 100644
index 0000000..f28ef67
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Rotate.cs
@@ -0,0 +1,70 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class RotateExtension{
+        /// <summary>
+        /// Rotate the tensor clockwise.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="k"> The times to rotate, which should be in range [1, 3]. The tensor will be rotated by k * 90 degrees. </param>
+        /// <param name="dimA"> The first dim of the matrix to rotate. </param>
+        /// <param name="dimB"> The second dim of the matrix to rotate. </param>
+        /// <returns></returns>
+        public static Tensor<T> Rotate<T>(this Tensor<T> src, int k, int dimA, int dimB) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, k, dimA, dimB));
+            res.TLayout.InitContiguousLayout();
+            RotateInternal(src, res, k, dimA, dimB);
+            return res;
+        }
+        private unsafe static void RotateInternal<T>(Tensor<T> src, Tensor<T> dst, int k, int dimA, int dimB) where T : struct, IEquatable<T>, IConvertible{
+            RotateParam param = new RotateParam(k, dimA, dimB);
+            IntPtr status = NativeExecutor.Execute(NativeApi.Rotate, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int k, int dimA, int dimB){
+            TensorLayout res = new TensorLayout();
+            if (k <= 0 || k > 3) {
+                throw new InvalidParamException("The k must be between 1 and 3.");
+            }
+            if (dimA == dimB) {
+                throw new InvalidParamException("The dimA and dimB cannot be the same.");
+            }
+            if (src.NDim <= 1) {
+                throw new MismatchedShapeException("The tensor to be rotated must have at least two dims.");
+            }
+            if (dimA < 0 || dimB < 0 || dimA >= src.NDim ||
+                dimB >= src.NDim) {
+                throw new InvalidParamException("The dimA or dimB exceeds the valid range.");
+            }
+            res.DType = src.DType;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                res.Shape[i] = src.Shape[i];
+            }
+            if (k != 2) {
+                (res.Shape[dimA], res.Shape[dimB]) = (res.Shape[dimB], res.Shape[dimA]);
+            }
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Rotate the tensor clockwise.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to be rotated. </param>
+        /// <param name="k"> The times to rotate, which should be in range [1, 3]. The tensor will be rotated by k * 90 degrees. </param>
+        /// <param name="dimA"> The first dim of the matrix to rotate. </param>
+        /// <param name="dimB"> The second dim of the matrix to rotate. </param>
+        /// <returns></returns>
+        public static Tensor<T> Rotate<T>(Tensor<T> src, int k, int dimA, int dimB) where T : struct, IEquatable<T>, IConvertible{
+            return src.Rotate(k, dimA, dimB);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Manipulation/Transpose.cs b/csharp/Tensor.NET/Manipulation/Transpose.cs
new file mode 100644
index 0000000..6c915a5
--- /dev/null
+++ b/csharp/Tensor.NET/Manipulation/Transpose.cs
@@ -0,0 +1,43 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class TransposeExtension{
+        /// <summary>
+        /// Reverse the two axes of a tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="dimA"></param>
+        /// <param name="dimB"></param>
+        /// <returns></returns>
+        public static Tensor<T> Transpose<T>(this Tensor<T> src, int dimA, int dimB) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, dimA, dimB));
+            res.TLayout.InitContiguousLayout();
+            TransposeInternal(src, res, dimA, dimB);
+            return res;
+        }
+        private unsafe static void TransposeInternal<T>(Tensor<T> src, Tensor<T> dst, int dimA, int dimB) where T : struct, IEquatable<T>, IConvertible{
+            TransposeParam param = new TransposeParam() { dimA = dimA, dimB = dimB };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Transpose, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int dimA, int dimB){
+            TensorLayout res = new TensorLayout();
+            if (dimA >= src.NDim || dimB >= src.NDim) {
+                throw new InvalidParamException("Invalid param for transpose.");
+            }
+            res.DType = src.DType;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                res.Shape[i] = src.Shape[i];
+            }
+            res.Shape[dimA] = src.Shape[dimB];
+            res.Shape[dimB] = src.Shape[dimA];
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicArith/Add.cs b/csharp/Tensor.NET/Math/BasicArith/Add.cs
new file mode 100644
index 0000000..d391676
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicArith/Add.cs
@@ -0,0 +1,105 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public static class AddExtension{
+        public static Tensor<double> Add(this Tensor<double> a, Tensor<double> b){
+            return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<double> Add(this Tensor<double> a, Tensor<float> b){
+            return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<double> Add(this Tensor<double> a, Tensor<long> b){
+            return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<double> Add(this Tensor<double> a, Tensor<int> b){
+            return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<double> Add(this Tensor<double> a, Tensor<bool> b){
+            return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x + (y ? 1 : 0));
+        }
+                            
+        public static Tensor<double> Add(this Tensor<float> a, Tensor<double> b){
+            return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<float> a, Tensor<float> b){
+            return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<float> a, Tensor<long> b){
+            return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<float> a, Tensor<int> b){
+            return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<float> a, Tensor<bool> b){
+            return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x + (y ? 1 : 0));
+        }
+                            
+        public static Tensor<double> Add(this Tensor<long> a, Tensor<double> b){
+            return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<long> a, Tensor<float> b){
+            return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<long> Add(this Tensor<long> a, Tensor<long> b){
+            return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<long> Add(this Tensor<long> a, Tensor<int> b){
+            return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<long> Add(this Tensor<long> a, Tensor<bool> b){
+            return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x + (y ? 1 : 0));
+        }
+                            
+        public static Tensor<double> Add(this Tensor<int> a, Tensor<double> b){
+            return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<int> a, Tensor<float> b){
+            return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<long> Add(this Tensor<int> a, Tensor<long> b){
+            return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<int> Add(this Tensor<int> a, Tensor<int> b){
+            return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x + y);
+        }
+                            
+        public static Tensor<int> Add(this Tensor<int> a, Tensor<bool> b){
+            return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x + (y ? 1 : 0));
+        }
+                            
+        public static Tensor<double> Add(this Tensor<bool> a, Tensor<double> b){
+            return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => (x ? 1 : 0) + y);
+        }
+                            
+        public static Tensor<float> Add(this Tensor<bool> a, Tensor<float> b){
+            return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => (x ? 1 : 0) + y);
+        }
+                            
+        public static Tensor<long> Add(this Tensor<bool> a, Tensor<long> b){
+            return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => (x ? 1 : 0) + y);
+        }
+                            
+        public static Tensor<int> Add(this Tensor<bool> a, Tensor<int> b){
+            return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => (x ? 1 : 0) + y);
+        }
+                            
+        public static Tensor<bool> Add(this Tensor<bool> a, Tensor<bool> b){
+            return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => x || y);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicArith/Div.cs b/csharp/Tensor.NET/Math/BasicArith/Div.cs
new file mode 100644
index 0000000..6e62c1e
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicArith/Div.cs
@@ -0,0 +1,105 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public static class DivExtension{
+        public static Tensor<double> Div(this Tensor<double> a, Tensor<double> b){
+            return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<double> a, Tensor<float> b){
+            return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<double> a, Tensor<long> b){
+            return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<double> a, Tensor<int> b){
+            return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<double> a, Tensor<bool> b){
+            return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => y ? x : Double.NaN);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<float> a, Tensor<double> b){
+            return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<float> a, Tensor<float> b){
+            return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<float> a, Tensor<long> b){
+            return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<float> a, Tensor<int> b){
+            return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<float> a, Tensor<bool> b){
+            return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => y ? x : Single.NaN);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<long> a, Tensor<double> b){
+            return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<long> a, Tensor<float> b){
+            return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<long> a, Tensor<long> b){
+            return InterElemOperation.Execute<long, long, double>(a, b, (x, y) => (double)x / (double)y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<long> a, Tensor<int> b){
+            return InterElemOperation.Execute<long, int, double>(a, b, (x, y) => (double)x / (double)y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<long> a, Tensor<bool> b){
+            return InterElemOperation.Execute<long, bool, double>(a, b, (x, y) => y ? x : Double.NaN);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<int> a, Tensor<double> b){
+            return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<int> a, Tensor<float> b){
+            return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x / y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<int> a, Tensor<long> b){
+            return InterElemOperation.Execute<int, long, double>(a, b, (x, y) => (double)x / (double)y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<int> a, Tensor<int> b){
+            return InterElemOperation.Execute<int, int, double>(a, b, (x, y) => (double)x / (double)y);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<int> a, Tensor<bool> b){
+            return InterElemOperation.Execute<int, bool, double>(a, b, (x, y) => y ? x : Double.NaN);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<bool> a, Tensor<double> b){
+            return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => x ? 1 / y : 0);
+        }
+                            
+        public static Tensor<float> Div(this Tensor<bool> a, Tensor<float> b){
+            return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => x ? 1 / y : 0);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<bool> a, Tensor<long> b){
+            return InterElemOperation.Execute<bool, long, double>(a, b, (x, y) => x ? 1 / (double)y : 0);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<bool> a, Tensor<int> b){
+            return InterElemOperation.Execute<bool, int, double>(a, b, (x, y) => x ? 1 / (double)y : 0);
+        }
+                            
+        public static Tensor<double> Div(this Tensor<bool> a, Tensor<bool> b){
+            return InterElemOperation.Execute<bool, bool, double>(a, b, (x, y) => y ? (x ? 1 : 0) : Double.NaN);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicArith/Mod.cs b/csharp/Tensor.NET/Math/BasicArith/Mod.cs
new file mode 100644
index 0000000..8fb4c3d
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicArith/Mod.cs
@@ -0,0 +1,105 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public static class ModExtension{
+        public static Tensor<double> Mod(this Tensor<double> a, Tensor<double> b){
+            return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<double> a, Tensor<float> b){
+            return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<double> a, Tensor<long> b){
+            return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<double> a, Tensor<int> b){
+            return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<double> a, Tensor<bool> b){
+            return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => y ? (x % 1) : Double.NaN);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<float> a, Tensor<double> b){
+            return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<float> a, Tensor<float> b){
+            return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<float> a, Tensor<long> b){
+            return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<float> a, Tensor<int> b){
+            return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<float> a, Tensor<bool> b){
+            return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => y ? (x % 1) : Single.NaN);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<long> a, Tensor<double> b){
+            return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<long> a, Tensor<float> b){
+            return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<long> Mod(this Tensor<long> a, Tensor<long> b){
+            return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<long> Mod(this Tensor<long> a, Tensor<int> b){
+            return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<long> Mod(this Tensor<long> a, Tensor<bool> b){
+            return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => 0);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<int> a, Tensor<double> b){
+            return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<int> a, Tensor<float> b){
+            return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<long> Mod(this Tensor<int> a, Tensor<long> b){
+            return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<int> Mod(this Tensor<int> a, Tensor<int> b){
+            return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x % y);
+        }
+                            
+        public static Tensor<int> Mod(this Tensor<int> a, Tensor<bool> b){
+            return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => 0);
+        }
+                            
+        public static Tensor<double> Mod(this Tensor<bool> a, Tensor<double> b){
+            return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => (x ? 1 : 0) % y);
+        }
+                            
+        public static Tensor<float> Mod(this Tensor<bool> a, Tensor<float> b){
+            return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => (x ? 1 : 0) % y);
+        }
+                            
+        public static Tensor<long> Mod(this Tensor<bool> a, Tensor<long> b){
+            return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => (x ? 1 : 0) % y);
+        }
+                            
+        public static Tensor<int> Mod(this Tensor<bool> a, Tensor<int> b){
+            return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => (x ? 1 : 0) % y);
+        }
+                            
+        public static Tensor<bool> Mod(this Tensor<bool> a, Tensor<bool> b){
+            return InterElemOperation.Execute<bool, bool, bool>(a, b, (x, y) => false);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicArith/Sub.cs b/csharp/Tensor.NET/Math/BasicArith/Sub.cs
new file mode 100644
index 0000000..b31d44d
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicArith/Sub.cs
@@ -0,0 +1,105 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public static class SubExtension{
+        public static Tensor<double> Sub(this Tensor<double> a, Tensor<double> b){
+            return InterElemOperation.Execute<double, double, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<double> a, Tensor<float> b){
+            return InterElemOperation.Execute<double, float, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<double> a, Tensor<long> b){
+            return InterElemOperation.Execute<double, long, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<double> a, Tensor<int> b){
+            return InterElemOperation.Execute<double, int, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<double> a, Tensor<bool> b){
+            return InterElemOperation.Execute<double, bool, double>(a, b, (x, y) => x - (y ? 1: 0));
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<float> a, Tensor<double> b){
+            return InterElemOperation.Execute<float, double, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<float> a, Tensor<float> b){
+            return InterElemOperation.Execute<float, float, float>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<float> a, Tensor<long> b){
+            return InterElemOperation.Execute<float, long, float>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<float> a, Tensor<int> b){
+            return InterElemOperation.Execute<float, int, float>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<float> a, Tensor<bool> b){
+            return InterElemOperation.Execute<float, bool, float>(a, b, (x, y) => x - (y ? 1: 0));
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<long> a, Tensor<double> b){
+            return InterElemOperation.Execute<long, double, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<long> a, Tensor<float> b){
+            return InterElemOperation.Execute<long, float, float>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<long> Sub(this Tensor<long> a, Tensor<long> b){
+            return InterElemOperation.Execute<long, long, long>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<long> Sub(this Tensor<long> a, Tensor<int> b){
+            return InterElemOperation.Execute<long, int, long>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<long> Sub(this Tensor<long> a, Tensor<bool> b){
+            return InterElemOperation.Execute<long, bool, long>(a, b, (x, y) => x - (y ? 1: 0));
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<int> a, Tensor<double> b){
+            return InterElemOperation.Execute<int, double, double>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<int> a, Tensor<float> b){
+            return InterElemOperation.Execute<int, float, float>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<long> Sub(this Tensor<int> a, Tensor<long> b){
+            return InterElemOperation.Execute<int, long, long>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<int> Sub(this Tensor<int> a, Tensor<int> b){
+            return InterElemOperation.Execute<int, int, int>(a, b, (x, y) => x - y);
+        }
+                            
+        public static Tensor<int> Sub(this Tensor<int> a, Tensor<bool> b){
+            return InterElemOperation.Execute<int, bool, int>(a, b, (x, y) => x - (y ? 1: 0));
+        }
+                            
+        public static Tensor<double> Sub(this Tensor<bool> a, Tensor<double> b){
+            return InterElemOperation.Execute<bool, double, double>(a, b, (x, y) => (x ? 1: 0) - y);
+        }
+                            
+        public static Tensor<float> Sub(this Tensor<bool> a, Tensor<float> b){
+            return InterElemOperation.Execute<bool, float, float>(a, b, (x, y) => (x ? 1: 0) - y);
+        }
+                            
+        public static Tensor<long> Sub(this Tensor<bool> a, Tensor<long> b){
+            return InterElemOperation.Execute<bool, long, long>(a, b, (x, y) => (x ? 1: 0) - y);
+        }
+                            
+        public static Tensor<int> Sub(this Tensor<bool> a, Tensor<int> b){
+            return InterElemOperation.Execute<bool, int, int>(a, b, (x, y) => (x ? 1: 0) - y);
+        }
+        [Obsolete("Sub operation cannot be applied to two bool Tensor, please consider using Xor instead.", true)]          
+        public static Tensor<bool> Sub(this Tensor<bool> a, Tensor<bool> b){
+            throw new InvalidOperationException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Abs.cs b/csharp/Tensor.NET/Math/BasicFunction/Abs.cs
new file mode 100644
index 0000000..debc843
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Abs.cs
@@ -0,0 +1,21 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static class AbsExtension{
+        public static Tensor<double> Add(this Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Abs(x));
+        }
+        public static Tensor<float> Add(this Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.Math.Abs(x));
+        }
+        public static Tensor<long> Add(this Tensor<long> inp){
+            return OnElemOperation.Execute<long, long>(inp, x => System.Math.Abs(x));
+        }
+        public static Tensor<int> Add(this Tensor<int> inp){
+            return OnElemOperation.Execute<int, int>(inp, x => System.Math.Abs(x));
+        }
+        public static Tensor<bool> Add(this Tensor<bool> inp){
+            return OnElemOperation.Execute<bool, bool>(inp, x => x);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Ceil.cs b/csharp/Tensor.NET/Math/BasicFunction/Ceil.cs
new file mode 100644
index 0000000..ff39a3d
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Ceil.cs
@@ -0,0 +1,12 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static class CeilExtension{
+        public static Tensor<double> Ceil(this Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Ceiling(x));
+        }
+        public static Tensor<float> Ceil(this Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Ceiling(x));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Clamp.cs b/csharp/Tensor.NET/Math/BasicFunction/Clamp.cs
new file mode 100644
index 0000000..46b85dc
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Clamp.cs
@@ -0,0 +1,18 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static partial class MathT{
+        public static Tensor<double> Clamp(Tensor<double> inp, double min, double max){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Clamp(x, min, max));
+        }
+        public static Tensor<float> Clamp(Tensor<float> inp, float min, float max){
+            return OnElemOperation.Execute<float, float>(inp, x => System.Math.Clamp(x, min, max));
+        }
+        public static Tensor<long> Clamp(Tensor<long> inp, long min, long max){
+            return OnElemOperation.Execute<long, long>(inp, x => System.Math.Clamp(x, min, max));
+        }
+        public static Tensor<int> Clamp(Tensor<int> inp, int min, int max){
+            return OnElemOperation.Execute<int, int>(inp, x => System.Math.Clamp(x, min, max));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Exp.cs b/csharp/Tensor.NET/Math/BasicFunction/Exp.cs
new file mode 100644
index 0000000..c4b771f
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Exp.cs
@@ -0,0 +1,21 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static partial class MathT{
+        public static Tensor<double> Exp(Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Exp(x));
+        }
+        public static Tensor<float> Exp(Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Exp(x));
+        }
+        public static Tensor<double> Exp(Tensor<long> inp){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Exp(x));
+        }
+        public static Tensor<double> Exp(Tensor<int> inp){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Exp(x));
+        }
+        public static Tensor<double> Exp(Tensor<bool> inp){
+            return OnElemOperation.Execute<bool, double>(inp, x => x ? System.Math.E : 1);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Floor.cs b/csharp/Tensor.NET/Math/BasicFunction/Floor.cs
new file mode 100644
index 0000000..30ebf55
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Floor.cs
@@ -0,0 +1,13 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static class FloorExtension{
+        public static Tensor<double> Floor(this Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Floor(x));
+        }
+        public static Tensor<float> Floor(this Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Floor(x));
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Log.cs b/csharp/Tensor.NET/Math/BasicFunction/Log.cs
new file mode 100644
index 0000000..6e18b05
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Log.cs
@@ -0,0 +1,30 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static partial class MathT{
+        public static Tensor<double> Log(Tensor<double> inp, double y){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log(y, x));
+        }
+        public static Tensor<float> Log(Tensor<float> inp, float y){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Log(y, x));
+        }
+        public static Tensor<double> Log(Tensor<long> inp, double y){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Log(y, x));
+        }
+        public static Tensor<double> Log(Tensor<int> inp, double y){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Log(y, x));
+        }
+        public static Tensor<double> Log(double baseValue, Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log(x, baseValue));
+        }
+        public static Tensor<float> Log(float baseValue, Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Log(x, baseValue));
+        }
+        public static Tensor<double> Log(double baseValue, Tensor<long> inp){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Log(x, baseValue));
+        }
+        public static Tensor<double> Log(double baseValue, Tensor<int> inp){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Log(x, baseValue));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Log10.cs b/csharp/Tensor.NET/Math/BasicFunction/Log10.cs
new file mode 100644
index 0000000..fafb04f
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Log10.cs
@@ -0,0 +1,18 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static partial class MathT{
+        public static Tensor<double> Log10(Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log10(x));
+        }
+        public static Tensor<float> Log10(Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Log10(x));
+        }
+        public static Tensor<double> Log10(Tensor<long> inp){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Log10(x));
+        }
+        public static Tensor<double> Log10(Tensor<int> inp){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Log10(x));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Log2.cs b/csharp/Tensor.NET/Math/BasicFunction/Log2.cs
new file mode 100644
index 0000000..5e053bc
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Log2.cs
@@ -0,0 +1,18 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static partial class MathT{
+        public static Tensor<double> Log2(Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Log2(x));
+        }
+        public static Tensor<float> Log2(Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Log2(x));
+        }
+        public static Tensor<double> Log2(Tensor<long> inp){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Log2(x));
+        }
+        public static Tensor<double> Log2(Tensor<int> inp){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Log2(x));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Pow.cs b/csharp/Tensor.NET/Math/BasicFunction/Pow.cs
new file mode 100644
index 0000000..dfb7d97
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Pow.cs
@@ -0,0 +1,56 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static partial class MathT{
+        public static Tensor<double> Pow(Tensor<double> inp, int y){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Pow(x, y));
+        }
+        public static Tensor<float> Pow(Tensor<float> inp, int y){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Pow(x, y));
+        }
+        public static Tensor<double> Pow(Tensor<long> inp, int y){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Pow(x, y));
+        }
+        public static Tensor<double> Pow(Tensor<int> inp, int y){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Pow(x, y));
+        }
+        public static Tensor<double> Pow(Tensor<double> inp, double y){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Pow(x, y));
+        }
+        public static Tensor<float> Pow(Tensor<float> inp, double y){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Pow(x, (float)y));
+        }
+        public static Tensor<double> Pow(Tensor<long> inp, double y){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Pow(x, y));
+        }
+        public static Tensor<double> Pow(Tensor<int> inp, double y){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Pow(x, y));
+        }
+
+
+        public static Tensor<double> Pow(int y, Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Pow(y, x));
+        }
+        public static Tensor<float> Pow(int y, Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Pow(y, x));
+        }
+        public static Tensor<double> Pow(int y, Tensor<long> inp){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Pow(y, x));
+        }
+        public static Tensor<double> Pow(int y, Tensor<int> inp){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Pow(y, x));
+        }
+        public static Tensor<double> Pow(double y, Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Pow(y, x));
+        }
+        public static Tensor<float> Pow(double y, Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Pow((float)y, x));
+        }
+        public static Tensor<double> Pow(double y, Tensor<long> inp){
+            return OnElemOperation.Execute<long, double>(inp, x => System.Math.Pow(y, x));
+        }
+        public static Tensor<double> Pow(double y, Tensor<int> inp){
+            return OnElemOperation.Execute<int, double>(inp, x => System.Math.Pow(y, x));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Math/BasicFunction/Round.cs b/csharp/Tensor.NET/Math/BasicFunction/Round.cs
new file mode 100644
index 0000000..c49e473
--- /dev/null
+++ b/csharp/Tensor.NET/Math/BasicFunction/Round.cs
@@ -0,0 +1,12 @@
+using Tensornet.Common;
+
+namespace Tensornet.Math{
+    public static class RoundExtension{
+        public static Tensor<double> Round(this Tensor<double> inp){
+            return OnElemOperation.Execute<double, double>(inp, x => System.Math.Round(x));
+        }
+        public static Tensor<float> Round(this Tensor<float> inp){
+            return OnElemOperation.Execute<float, float>(inp, x => System.MathF.Round(x));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Native/NativeApi.cs b/csharp/Tensor.NET/Native/NativeApi.cs
new file mode 100644
index 0000000..f36f37b
--- /dev/null
+++ b/csharp/Tensor.NET/Native/NativeApi.cs
@@ -0,0 +1,74 @@
+using System.Runtime.InteropServices;
+
+namespace Tensornet.Native{
+    internal static class NativeApi{
+        [DllImport("libtensornet")]
+        public static extern StatusCode GetErrorCode(IntPtr status);
+        [DllImport("libtensornet")]
+        public static extern IntPtr GetErrorMessage(IntPtr status);
+        [DllImport("libtensornet")]
+        public static extern void FreeStatusMemory(IntPtr status);
+
+
+        [DllImport("libtensornet")]
+        public static extern IntPtr Matmul(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Dot(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr BoolIndex(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Interelem(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
+
+
+        [DllImport("libtensornet")]
+        public static extern IntPtr Permute(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Transpose(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Argmxx(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Repeat(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Flip(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr MatrixInverse(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Rotate(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Pad(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Sort(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Onehot(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Sum(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Mean(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Max(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Min(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Negative(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+
+
+        [DllImport("libtensornet")]
+        public static extern IntPtr TypeConvert(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+
+        [DllImport("libtensornet")]
+        public static extern IntPtr Concat(IntPtr inps, int size, IntPtr oup, IntPtr param, NativeProvider provider);
+
+        [DllImport("libtensornet")]
+        public static extern IntPtr Normal(IntPtr t, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Uniform(IntPtr t, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Eye(IntPtr t, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Fill(IntPtr t, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Arange(IntPtr t, IntPtr param, NativeProvider provider);
+        [DllImport("libtensornet")]
+        public static extern IntPtr Linspace(IntPtr t, IntPtr param, NativeProvider provider);
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Native/NativeExecutor.cs b/csharp/Tensor.NET/Native/NativeExecutor.cs
new file mode 100644
index 0000000..ef20819
--- /dev/null
+++ b/csharp/Tensor.NET/Native/NativeExecutor.cs
@@ -0,0 +1,108 @@
+using System.Buffers;
+using Tensornet.Common;
+
+namespace Tensornet.Native{
+    internal class NativeExecutor{
+        internal delegate IntPtr DoubleInputOperation(IntPtr a, IntPtr b, IntPtr oup, IntPtr param, NativeProvider provider);
+        internal delegate IntPtr SingleInputOperation(IntPtr inp, IntPtr oup, IntPtr param, NativeProvider provider);
+        internal delegate IntPtr SelfModifyOperation(IntPtr t, IntPtr param, NativeProvider provider);
+        private NativeExecutor() { }
+        unsafe internal static IntPtr Execute(DoubleInputOperation func, ITensorMemory a, ITensorMemory b, ITensorMemory oup, 
+                            TensorLayout layoutA, TensorLayout layoutB, TensorLayout layoutOup, IntPtr param, NativeProvider provider){
+            MemoryHandle handleA, handleB, handleOup;
+            a.Pin(out handleA);
+            b.Pin(out handleB);
+            oup.Pin(out handleOup);
+            IntPtr status;
+            fixed(int* shapeAPtr = layoutA.Shape, shapeBPtr = layoutB.Shape, shapeOupPtr = layoutOup.Shape, 
+                        strideAPtr = layoutA.Stride, strideBPtr = layoutB.Stride, strideOupPtr = layoutOup.Stride){
+                NativeTensor nativeA = new NativeTensor()
+                {
+                    dtype = layoutA.DType,
+                    ndim = layoutA.NDim,
+                    offset = layoutA.Offset * TensorTypeInfo.GetTypeSize(layoutA.DType),
+                    shape = new IntPtr(shapeAPtr),
+                    stride = new IntPtr(strideAPtr),
+                    data = new IntPtr(handleA.Pointer)
+                };
+                NativeTensor nativeB = new NativeTensor()
+                {
+                    dtype = layoutB.DType,
+                    ndim = layoutB.NDim,
+                    offset = layoutB.Offset * TensorTypeInfo.GetTypeSize(layoutB.DType),
+                    shape = new IntPtr(shapeBPtr),
+                    stride = new IntPtr(strideBPtr),
+                    data = new IntPtr(handleB.Pointer)
+                };
+                NativeTensor nativeOup = new NativeTensor()
+                {
+                    dtype = layoutOup.DType,
+                    ndim = layoutOup.NDim,
+                    offset = layoutOup.Offset * TensorTypeInfo.GetTypeSize(layoutOup.DType),
+                    shape = new IntPtr(shapeOupPtr),
+                    stride = new IntPtr(strideOupPtr),
+                    data = new IntPtr(handleOup.Pointer)
+                };
+                status = func(new IntPtr(&nativeA), new IntPtr(&nativeB), new IntPtr(&nativeOup), param, provider);
+            }
+            handleA.Dispose();
+            handleB.Dispose();
+            handleOup.Dispose();
+            return status;
+        }
+
+        unsafe internal static IntPtr Execute(SingleInputOperation func, ITensorMemory inp, ITensorMemory oup, TensorLayout layoutInp, 
+                                                    TensorLayout layoutOup, IntPtr param, NativeProvider provider){
+            MemoryHandle handleInp, handleOup;
+            inp.Pin(out handleInp);
+            oup.Pin(out handleOup);
+            IntPtr status;
+            fixed(int* shapeInpPtr = layoutInp.Shape, shapeOupPtr = layoutOup.Shape, 
+                        strideInpPtr = layoutInp.Stride, strideOupPtr = layoutOup.Stride){
+                NativeTensor nativeInp = new NativeTensor()
+                {
+                    dtype = layoutInp.DType,
+                    ndim = layoutInp.NDim,
+                    offset = layoutInp.Offset * TensorTypeInfo.GetTypeSize(layoutInp.DType),
+                    shape = new IntPtr(shapeInpPtr),
+                    stride = new IntPtr(strideInpPtr),
+                    data = new IntPtr(handleInp.Pointer)
+                };
+                NativeTensor nativeOup = new NativeTensor()
+                {
+                    dtype = layoutOup.DType,
+                    ndim = layoutOup.NDim,
+                    offset = layoutOup.Offset * TensorTypeInfo.GetTypeSize(layoutOup.DType),
+                    shape = new IntPtr(shapeOupPtr),
+                    stride = new IntPtr(strideOupPtr),
+                    data = new IntPtr(handleOup.Pointer)
+                };
+                status = func(new IntPtr(&nativeInp), new IntPtr(&nativeOup), param, provider);
+            }
+            handleInp.Dispose();
+            handleOup.Dispose();
+            return status;
+        }
+
+        unsafe internal static IntPtr Execute(SelfModifyOperation func, ITensorMemory tm, TensorLayout layout, IntPtr param, NativeProvider provider){
+            MemoryHandle mhandle;
+            tm.Pin(out mhandle);
+            IntPtr status;
+            fixed(int* shapeInpPtr = layout.Shape, shapeOupPtr = layout.Shape, 
+                        strideInpPtr = layout.Stride, strideOupPtr = layout.Stride){
+                NativeTensor nativeTensor = new NativeTensor()
+                {
+                    dtype = layout.DType,
+                    ndim = layout.NDim,
+                    offset = layout.Offset * TensorTypeInfo.GetTypeSize(layout.DType),
+                    shape = new IntPtr(shapeInpPtr),
+                    stride = new IntPtr(strideInpPtr),
+                    data = new IntPtr(mhandle.Pointer)
+                };
+                status = func(new IntPtr(&nativeTensor), param, provider);
+            }
+            mhandle.Dispose();
+            return status;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Native/NativeParam.cs b/csharp/Tensor.NET/Native/NativeParam.cs
new file mode 100644
index 0000000..85d2c52
--- /dev/null
+++ b/csharp/Tensor.NET/Native/NativeParam.cs
@@ -0,0 +1,102 @@
+using Tensornet.Exceptions;
+using Tensornet.Common;
+
+namespace Tensornet.Native.Param{
+    internal struct MatmulParam{
+        
+    }
+    internal struct DotParam{
+        
+    }
+    internal struct InterelemParam{
+        internal InterElemOperationType operationType;
+    }
+    internal struct PermuteParam{
+        internal IntPtr dims;
+    }
+    internal struct TransposeParam{
+        internal int dimA;
+        internal int dimB;
+    }
+    internal struct ArgmxxParam{
+        internal int axis;
+        internal bool isMax;
+    }
+    internal struct RepeatParam{
+        internal int repeats;
+        internal int axis;
+    }
+    internal struct FlipParam{
+        internal IntPtr dims;
+    }
+    internal struct RotateParam{
+        internal int k;
+        internal int dimA;
+        internal int dimB;
+        internal RotateParam(int k, int dimA, int dimB){
+            this.k = k;
+            if(dimA > dimB){
+                (this.dimA, this.dimB) = (dimB, dimA);
+            }
+            else{
+                (this.dimA, this.dimB) = (dimA, dimB);
+            }
+        }
+    }
+    internal struct PadParam{
+        internal PadMode mode;
+        internal int size;
+        internal IntPtr widths;
+        internal IntPtr constants;
+    }
+    internal struct SortParam{
+        internal int axis;
+        internal SortOrder order;
+    }
+    internal struct OnehotParam{
+        internal int maxValue;
+    }
+    internal struct SumParam{
+        internal IntPtr dims;
+    }
+    internal struct MeanParam{
+        internal IntPtr dims;
+    }
+    internal struct MaxParam{
+        internal IntPtr dims;
+    }
+    internal struct MinParam{
+        internal IntPtr dims;
+    }
+    internal struct TypeConvertParam{
+        internal DType targetType;
+    }
+    internal struct NormalParam{
+        internal double mean;
+        internal double std;
+    }
+    internal struct UniformParam{
+        internal double minValue;
+        internal double maxValue;
+    }
+    internal struct EyeParam{
+        internal int k;
+    }
+    internal struct FillParam{
+        internal double value;
+    }
+    internal struct ArangeParam{
+        internal double start;
+        internal double stop;
+        internal double step;
+    }
+    internal struct LinspaceParam{
+        internal double start;
+        internal double stop;
+        internal int num;
+        internal bool isEndpoint;
+    }
+    internal struct ConcatParam{
+        internal int axis;
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Native/NativeStatus.cs b/csharp/Tensor.NET/Native/NativeStatus.cs
similarity index 83%
rename from csharp/Num.NET/Native/NativeStatus.cs
rename to csharp/Tensor.NET/Native/NativeStatus.cs
index 7a6646f..bbe86ca 100644
--- a/csharp/Num.NET/Native/NativeStatus.cs
+++ b/csharp/Tensor.NET/Native/NativeStatus.cs
@@ -1,6 +1,6 @@
 using System.Runtime.InteropServices;
 
-namespace Numnet.Native{
+namespace Tensornet.Native{
     internal enum StatusCode: Int32
     {
         OK = 0,
@@ -20,7 +20,7 @@ internal static class NativeStatus{
         /// Assert that the status indicates no error. Note that the memory that "status" points to will be free after this call.
         /// </summary>
         /// <param name="status"></param>
-        /// <exception cref="Numnet.Exceptions.NNRuntimeException"></exception>
+        /// <exception cref="Tensornet.Exceptions.NNRuntimeException"></exception>
         public static void AssertOK(IntPtr status){
             if(status == IntPtr.Zero){
                 return;
@@ -28,7 +28,7 @@ public static void AssertOK(IntPtr status){
             var errorCode = GetErrorCode(status);
             var errorMsg = GetErrorMessage(status);
             DisposeStatus(status);
-            throw new Numnet.Exceptions.NNRuntimeException($"A runtime error occured, [{Enum.GetName(typeof(StatusCode), errorCode)}]: {errorMsg}");
+            throw new Tensornet.Exceptions.NNRuntimeException($"A runtime error occured, [{Enum.GetName(typeof(StatusCode), errorCode)}]: {errorMsg}");
         }
 
         public static void DisposeStatus(IntPtr status){
@@ -37,7 +37,6 @@ public static void DisposeStatus(IntPtr status){
 
 
         public static StatusCode GetErrorCode(IntPtr status){
-            Console.WriteLine("Get!");
             return status == IntPtr.Zero?StatusCode.OK : NativeApi.GetErrorCode(status);
         }
 
diff --git a/csharp/Num.NET/Native/NativeTensor.cs b/csharp/Tensor.NET/Native/NativeTensor.cs
similarity index 68%
rename from csharp/Num.NET/Native/NativeTensor.cs
rename to csharp/Tensor.NET/Native/NativeTensor.cs
index 4705ee8..9f831cf 100644
--- a/csharp/Num.NET/Native/NativeTensor.cs
+++ b/csharp/Tensor.NET/Native/NativeTensor.cs
@@ -1,7 +1,11 @@
 using System.Runtime.InteropServices;
 
-namespace Numnet.Native{
+namespace Tensornet.Native{
     public enum NativeProvider: Int32 { Naive = 0, ST_x86 = 1, MT_x86 = 2 };
+    /// <summary>
+    /// Do not change the order unless you want to refactor the type scheme!
+    /// If you change the order, you should also modify the code in core/op/common/dtype_deduce.h
+    /// </summary>
     public enum DType: Int32
     {
         Invalid = 0,
diff --git a/csharp/Tensor.NET/Operators/Add.cs b/csharp/Tensor.NET/Operators/Add.cs
new file mode 100644
index 0000000..b33b4ea
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Add.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator+(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Add);
+        }
+        public static Tensor<T> operator+(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Add);
+        }
+        public static Tensor<T> operator+(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Add);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/And.cs b/csharp/Tensor.NET/Operators/And.cs
new file mode 100644
index 0000000..4192a60
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/And.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator&(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.And);
+        }
+        public static Tensor<T> operator&(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.And);
+        }
+        public static Tensor<T> operator&(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.And);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Div.cs b/csharp/Tensor.NET/Operators/Div.cs
new file mode 100644
index 0000000..68c512b
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Div.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator/(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Div);
+        }
+        public static Tensor<T> operator/(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Div);
+        }
+        public static Tensor<T> operator/(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Div);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Equals.cs b/csharp/Tensor.NET/Operators/Equals.cs
new file mode 100644
index 0000000..acee15c
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Equals.cs
@@ -0,0 +1,69 @@
+using Tensornet.Math;
+using Tensornet.Common;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<bool> operator==(Tensor<T> lhs, Tensor<double> rhs){
+            return InterElemOperation.Execute<T, double, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) < 1e-9);
+        }
+        public static Tensor<bool> operator!=(Tensor<T> lhs, Tensor<double> rhs){
+            return InterElemOperation.Execute<T, double, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) >= 1e-9);
+        }
+        public static Tensor<bool> operator==(Tensor<T> lhs, Tensor<float> rhs){
+            return InterElemOperation.Execute<T, float, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) < 1e-9);
+        }
+        public static Tensor<bool> operator!=(Tensor<T> lhs, Tensor<float> rhs){
+            return InterElemOperation.Execute<T, float, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) >= 1e-9);
+        }
+        public static Tensor<bool> operator==(Tensor<T> lhs, Tensor<long> rhs){
+            return InterElemOperation.Execute<T, long, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) < 1e-9);
+        }
+        public static Tensor<bool> operator!=(Tensor<T> lhs, Tensor<long> rhs){
+            return InterElemOperation.Execute<T, long, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) >= 1e-9);
+        }
+        public static Tensor<bool> operator==(Tensor<T> lhs, Tensor<int> rhs){
+            return InterElemOperation.Execute<T, int, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) < 1e-9);
+        }
+        public static Tensor<bool> operator!=(Tensor<T> lhs, Tensor<int> rhs){
+            return InterElemOperation.Execute<T, int, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - y) >= 1e-9);
+        }
+        public static Tensor<bool> operator==(Tensor<T> lhs, Tensor<bool> rhs){
+            return InterElemOperation.Execute<T, bool, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - (y ? 1 : 0)) < 1e-9);
+        }
+        public static Tensor<bool> operator!=(Tensor<T> lhs, Tensor<bool> rhs){
+            return InterElemOperation.Execute<T, bool, bool>(lhs, rhs, (x, y) => System.Math.Abs(Convert.ToDouble(x) - (y ? 1 : 0)) >= 1e-9);
+        }
+
+        public static Tensor<bool> operator==(Tensor<T> lhs, double rhs){
+            return OnElemOperation.Execute<T, bool>(lhs, x => System.Math.Abs(Convert.ToDouble(x) - rhs) < 1e-9);
+        }
+        public static Tensor<bool> operator!=(Tensor<T> lhs, double rhs){
+            return OnElemOperation.Execute<T, bool>(lhs, x => System.Math.Abs(Convert.ToDouble(x) - rhs) >= 1e-9);
+        }
+
+        public override bool Equals(object? obj)
+        {
+            if (obj is null || GetType() != obj.GetType())
+            {
+                return false;
+            }
+            if (ReferenceEquals(this, obj)) return true;
+            return Equals((Tensor<T>) obj);
+        }
+
+        public override int GetHashCode()
+        {
+            int valueHash = DataType switch
+            {
+                DType.Bool => TMemory.GetHashCode() % 0x3f3f3f3f,
+                DType.Int32 => TMemory.GetHashCode() % 0x3f3f3f3f,
+                DType.Int64 => TMemory.GetHashCode() % 0x3f3f3f3f,
+                DType.Float32 => TMemory.GetHashCode() % 0x3f3f3f3f,
+                DType.Float64 => TMemory.GetHashCode() % 0x3f3f3f3f,
+                _ => throw new NotImplementedException()
+            };
+            return (valueHash + (int)DataType * 0x0cfcfcfc) % 0x3f3f3f3f;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Greater.cs b/csharp/Tensor.NET/Operators/Greater.cs
new file mode 100644
index 0000000..d602d44
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Greater.cs
@@ -0,0 +1,44 @@
+using Tensornet.Math;
+using Tensornet.Common;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<bool> operator>(Tensor<T> lhs, Tensor<double> rhs){
+            return InterElemOperation.Execute<T, double, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) > y);
+        }
+        public static Tensor<bool> operator>=(Tensor<T> lhs, Tensor<double> rhs){
+            return InterElemOperation.Execute<T, double, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) >= y);
+        }
+        public static Tensor<bool> operator>(Tensor<T> lhs, Tensor<float> rhs){
+            return InterElemOperation.Execute<T, float, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) > y);
+        }
+        public static Tensor<bool> operator>=(Tensor<T> lhs, Tensor<float> rhs){
+            return InterElemOperation.Execute<T, float, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) >= y);
+        }
+        public static Tensor<bool> operator>(Tensor<T> lhs, Tensor<long> rhs){
+            return InterElemOperation.Execute<T, long, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) > y);
+        }
+        public static Tensor<bool> operator>=(Tensor<T> lhs, Tensor<long> rhs){
+            return InterElemOperation.Execute<T, long, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) >= y);
+        }
+        public static Tensor<bool> operator>(Tensor<T> lhs, Tensor<int> rhs){
+            return InterElemOperation.Execute<T, int, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) > y);
+        }
+        public static Tensor<bool> operator>=(Tensor<T> lhs, Tensor<int> rhs){
+            return InterElemOperation.Execute<T, int, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) >= y);
+        }
+        public static Tensor<bool> operator>(Tensor<T> lhs, Tensor<bool> rhs){
+            return InterElemOperation.Execute<T, bool, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) > (y ? 1 : 0));
+        }
+        public static Tensor<bool> operator>=(Tensor<T> lhs, Tensor<bool> rhs){
+            return InterElemOperation.Execute<T, bool, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) >= (y ? 1 : 0));
+        }
+        public static Tensor<bool> operator>(Tensor<T> lhs, double rhs){
+            return OnElemOperation.Execute<T, bool>(lhs, x => Convert.ToDouble(x) > rhs);
+        }
+        public static Tensor<bool> operator>=(Tensor<T> lhs, double rhs){
+            return OnElemOperation.Execute<T, bool>(lhs, x => Convert.ToDouble(x) >= rhs);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Less.cs b/csharp/Tensor.NET/Operators/Less.cs
new file mode 100644
index 0000000..7ca9bb5
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Less.cs
@@ -0,0 +1,44 @@
+using Tensornet.Math;
+using Tensornet.Common;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<bool> operator<(Tensor<T> lhs, Tensor<double> rhs){
+            return InterElemOperation.Execute<T, double, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) < y);
+        }
+        public static Tensor<bool> operator<=(Tensor<T> lhs, Tensor<double> rhs){
+            return InterElemOperation.Execute<T, double, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) <= y);
+        }
+        public static Tensor<bool> operator<(Tensor<T> lhs, Tensor<float> rhs){
+            return InterElemOperation.Execute<T, float, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) < y);
+        }
+        public static Tensor<bool> operator<=(Tensor<T> lhs, Tensor<float> rhs){
+            return InterElemOperation.Execute<T, float, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) <= y);
+        }
+        public static Tensor<bool> operator<(Tensor<T> lhs, Tensor<long> rhs){
+            return InterElemOperation.Execute<T, long, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) < y);
+        }
+        public static Tensor<bool> operator<=(Tensor<T> lhs, Tensor<long> rhs){
+            return InterElemOperation.Execute<T, long, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) <= y);
+        }
+        public static Tensor<bool> operator<(Tensor<T> lhs, Tensor<int> rhs){
+            return InterElemOperation.Execute<T, int, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) < y);
+        }
+        public static Tensor<bool> operator<=(Tensor<T> lhs, Tensor<int> rhs){
+            return InterElemOperation.Execute<T, int, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) <= y);
+        }
+        public static Tensor<bool> operator<(Tensor<T> lhs, Tensor<bool> rhs){
+            return InterElemOperation.Execute<T, bool, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) < (y ? 1 : 0));
+        }
+        public static Tensor<bool> operator<=(Tensor<T> lhs, Tensor<bool> rhs){
+            return InterElemOperation.Execute<T, bool, bool>(lhs, rhs, (x, y) => Convert.ToDouble(x) <= (y ? 1 : 0));
+        }
+        public static Tensor<bool> operator<(Tensor<T> lhs, double rhs){
+            return OnElemOperation.Execute<T, bool>(lhs, x => Convert.ToDouble(x) < rhs);
+        }
+        public static Tensor<bool> operator<=(Tensor<T> lhs, double rhs){
+            return OnElemOperation.Execute<T, bool>(lhs, x => Convert.ToDouble(x) <= rhs);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Mod.cs b/csharp/Tensor.NET/Operators/Mod.cs
new file mode 100644
index 0000000..b8edc35
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Mod.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator%(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Mod);
+        }
+        public static Tensor<T> operator%(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Mod);
+        }
+        public static Tensor<T> operator%(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Mod);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Mul.cs b/csharp/Tensor.NET/Operators/Mul.cs
new file mode 100644
index 0000000..49eb991
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Mul.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator*(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Mul);
+        }
+        public static Tensor<T> operator*(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Mul);
+        }
+        public static Tensor<T> operator*(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Mul);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Negative.cs b/csharp/Tensor.NET/Operators/Negative.cs
new file mode 100644
index 0000000..39aec2f
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Negative.cs
@@ -0,0 +1,17 @@
+using Tensornet.Math;
+using Tensornet.Common;
+using Tensornet.Native;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator-(Tensor<T> self){
+            Tensor<T> res = new Tensor<T>(new TensorLayout(self.TLayout, true));
+            NegativeInternal(self, res);
+            return res;
+        }
+        public static unsafe void NegativeInternal(Tensor<T> src, Tensor<T> dst){
+            IntPtr status = NativeExecutor.Execute(NativeApi.Negative, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, IntPtr.Zero, Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Or.cs b/csharp/Tensor.NET/Operators/Or.cs
new file mode 100644
index 0000000..4bab2f0
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Or.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator|(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Or);
+        }
+        public static Tensor<T> operator|(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Or);
+        }
+        public static Tensor<T> operator|(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Or);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Sub.cs b/csharp/Tensor.NET/Operators/Sub.cs
new file mode 100644
index 0000000..4bb482e
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Sub.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator-(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Sub);
+        }
+        public static Tensor<T> operator-(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Sub);
+        }
+        public static Tensor<T> operator-(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Sub);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Operators/Xor.cs b/csharp/Tensor.NET/Operators/Xor.cs
new file mode 100644
index 0000000..5f32f59
--- /dev/null
+++ b/csharp/Tensor.NET/Operators/Xor.cs
@@ -0,0 +1,15 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        public static Tensor<T> operator^(Tensor<T> lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>(lhs, rhs, InterElemOperationType.Xor);
+        }
+        public static Tensor<T> operator^(Tensor<T> lhs, T rhs){
+            return InterElemOperation.Execute<T>(lhs, (Tensor<T>)rhs, InterElemOperationType.Xor);
+        }
+        public static Tensor<T> operator^(T lhs, Tensor<T> rhs){
+            return InterElemOperation.Execute<T>((Tensor<T>)lhs, rhs, InterElemOperationType.Xor);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Random/Normal.cs b/csharp/Tensor.NET/Random/Normal.cs
new file mode 100644
index 0000000..1f607c9
--- /dev/null
+++ b/csharp/Tensor.NET/Random/Normal.cs
@@ -0,0 +1,20 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        public static partial class Random{
+            public static Tensor<T> Normal<T>(TensorShape shape, double mean = .0, double std = .1) where T : struct, IConvertible, IEquatable<T>{
+                Tensor<T> res = new Tensor<T>(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+                NormalInternal<T>(res, mean, std);
+                return res;
+            }
+            private unsafe static void NormalInternal<T>(Tensor<T> t, double mean, double std) where T : struct, IConvertible, IEquatable<T>{
+                NormalParam param = new NormalParam() { mean = mean, std = std };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Normal, t.TMemory, t.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Random/Uniform.cs b/csharp/Tensor.NET/Random/Uniform.cs
new file mode 100644
index 0000000..f5dbb79
--- /dev/null
+++ b/csharp/Tensor.NET/Random/Uniform.cs
@@ -0,0 +1,28 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static partial class Tensor{
+        public static partial class Random{
+            /// <summary>
+            /// Genetate a randome Tensor with value in range [minValue, maxValue)
+            /// </summary>
+            /// <typeparam name="T"></typeparam>
+            /// <param name="shape"></param>
+            /// <param name="minValue"></param>
+            /// <param name="maxValue"></param>
+            /// <returns></returns>
+            public static Tensor<T> Uniform<T>(TensorShape shape, double minValue = .0, double maxValue = 1.0) where T : struct, IConvertible, IEquatable<T>{
+                Tensor<T> res = new Tensor<T>(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+                UniformInternal<T>(res, minValue, maxValue);
+                return res;
+            }
+            private unsafe static void UniformInternal<T>(Tensor<T> t, double minValue, double maxValue) where T : struct, IConvertible, IEquatable<T>{
+                UniformParam param = new UniformParam() { minValue = minValue, maxValue = maxValue };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Uniform, t.TMemory, t.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Argmxx.cs b/csharp/Tensor.NET/Statistics/Argmxx.cs
new file mode 100644
index 0000000..232a309
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Argmxx.cs
@@ -0,0 +1,85 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class ArgmaxExtension{
+        /// <summary>
+        /// Returns the indices of the maximum values along an axis.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.argmax.html?highlight=argmax#numpy.argmax
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get the indices. </param>
+        /// <returns></returns>
+        public static Tensor<long> Argmax<T>(this Tensor<T> src, int axis) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<long> res = new Tensor<long>(DeduceLayout(src.TLayout, axis));
+            res.TLayout.InitContiguousLayout();
+            ArgmxxInternal(src, res, axis, true);
+            res.TLayout.RemoveAxisInplace(axis);
+            return res;
+        }
+        /// <summary>
+        /// Returns the indices of the minimum values along an axis.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.argmin.html?highlight=argmin#numpy.argmin
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get the indices. </param>
+        /// <returns></returns>
+        public static Tensor<long> Argmin<T>(this Tensor<T> src, int axis) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<long> res = new Tensor<long>(DeduceLayout(src.TLayout, axis));
+            res.TLayout.InitContiguousLayout();
+            ArgmxxInternal(src, res, axis, false);
+            res.TLayout.RemoveAxisInplace(axis);
+            return res;
+        }
+
+        private unsafe static void ArgmxxInternal<T>(Tensor<T> src, Tensor<long> dst, int axis, bool isMax) where T : struct, IEquatable<T>, IConvertible{
+            ArgmxxParam param = new ArgmxxParam() { axis = axis, isMax = isMax };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Argmxx, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int axis){
+            TensorLayout res = new TensorLayout();
+            if (axis < 0 || axis >= src.NDim) {
+                throw new InvalidParamException("Invalid argmxx axis param.");
+            }
+            res.DType = DType.Int64;
+            res.NDim = src.NDim;
+            for (int i = 0; i < src.NDim; i++) {
+                res.Shape[i] = src.Shape[i];
+            }
+            res.Shape[axis] = 1;
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Returns the indices of the maximum values along an axis.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.argmax.html?highlight=argmax#numpy.argmax
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get the indices. </param>
+        /// <returns></returns>
+        public static Tensor<long> Argmax<T>(Tensor<T> src, int axis) where T : struct, IEquatable<T>, IConvertible{
+            return src.Argmax(axis);
+        }
+        /// <summary>
+        /// Returns the indices of the minimum values along an axis.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.argmin.html?highlight=argmin#numpy.argmin
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get the indices. </param>
+        /// <returns></returns>
+        public static Tensor<long> Argmin<T>(Tensor<T> src, int axis) where T : struct, IEquatable<T>, IConvertible{
+            return src.Argmin(axis);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Fill.cs b/csharp/Tensor.NET/Statistics/Fill.cs
new file mode 100644
index 0000000..8a5392e
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Fill.cs
@@ -0,0 +1,45 @@
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class FillExtension{
+        /// <summary>
+        /// Fill the tensor with the given value.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="value"> The value to be filled in the tensor. </param>
+        public static void Fill<T>(this Tensor<T> src, T value) where T : struct, IEquatable<T>, IConvertible
+        {
+            FillParam param = new FillParam() { value = Convert.ToDouble(value) };
+            FillInternal<T>(src, value);
+        }
+        private unsafe static void FillInternal<T>(Tensor<T> src, T value) where T : struct, IEquatable<T>, IConvertible{
+            FillParam param = new FillParam() { value = Convert.ToDouble(value) };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Fill, src.TMemory, src.TLayout, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        public static Tensor<TResult> FillLike<TResult, TRefer>(Tensor<TRefer> tensor, TResult value) 
+            where TResult : struct, IConvertible, IEquatable<TResult> 
+            where TRefer : struct, IConvertible, IEquatable<TRefer>{
+            Tensor<TResult> res = new Tensor<TResult>(tensor.TLayout);
+            res.Fill(value);
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Fill the tensor with the given value.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="value"> The value to be filled in the tensor. </param>
+        public static void Fill<T>(Tensor<T> src, T value) where T : struct, IEquatable<T>, IConvertible
+        {
+            src.Fill(value);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Max.cs b/csharp/Tensor.NET/Statistics/Max.cs
new file mode 100644
index 0000000..41502f9
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Max.cs
@@ -0,0 +1,126 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class MaxExtension{
+        /// <summary>
+        /// Get the maximum elements along some axes.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axes"> The axes to get maximum value. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Max<T>(this Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, axes));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            foreach(var axis in axes){
+                span[axis] = true;
+            }
+            MaxInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the maximum elements along an aixs.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axes to get maximum value. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Max<T>(this Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, axis));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            span[axis] = true;
+            MaxInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the maximum element of a tensor. A tensor with shape [1] will be returned by default.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Max<T>(this Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            boolDims.AsSpan().Fill(true);
+            MaxInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        private unsafe static void MaxInternal<T>(Tensor<T> src, Tensor<T> dst, bool[] dims, bool keepDims) where T : struct, IEquatable<T>, IConvertible{
+            fixed(bool* ptr = dims){
+                MaxParam p = new MaxParam() { dims = new IntPtr(ptr) };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Max, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+            if(!keepDims){
+                dst.TLayout.RemoveAllDanglingAxisInplace();
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int[] axes){
+            var res = new TensorLayout(src, true);
+            foreach(var dim in axes){
+                res.Shape[dim] = 1;
+            }
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int axis){
+            var res = new TensorLayout(src, true);
+            res.Shape[axis] = 1;
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            var res = new TensorLayout(src, true);
+            res.Shape.AsSpan().Fill(1);
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <Summary>
+        /// Get the maximum elements of the tensor along some axes.
+        /// </Summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to get maximum elements. </param>
+        /// <param name="axes"> The axes to get mamimum elements. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Max<T>(Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Max(axes, keepDims);
+        }
+        /// <Summary>
+        /// Get the maximum elements of the tensor along an axis.
+        /// </Summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to get maximum elements. </param>
+        /// <param name="axis"> The axis to get maximum elements. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Max<T>(Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Max(axis, keepDims);
+        }
+        /// <Summary>
+        /// Get the maximum elements of the tensor.
+        /// </Summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to get maximum element. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Max<T>(Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Max(keepDims);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Mean.cs b/csharp/Tensor.NET/Statistics/Mean.cs
new file mode 100644
index 0000000..852d9c5
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Mean.cs
@@ -0,0 +1,126 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class MeanExtension{
+        /// <summary>
+        /// Get the mean of elements of a tensor along some axes.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axes"> The axes to get mean. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<double> Mean<T>(this Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<double> res = new Tensor<double>(DeduceLayout(src.TLayout, axes));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            foreach(var axis in axes){
+                span[axis] = true;
+            }
+            MeanInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the mean of elements of a tensor along an axis.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get mean. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<double> Mean<T>(this Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<double> res = new Tensor<double>(DeduceLayout(src.TLayout, axis));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            span[axis] = true;
+            MeanInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the mean of elements of a tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<double> Mean<T>(this Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<double> res = new Tensor<double>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            boolDims.AsSpan().Fill(true);
+            MeanInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        private unsafe static void MeanInternal<T>(Tensor<T> src, Tensor<double> dst, bool[] dims, bool keepDims) where T : struct, IEquatable<T>, IConvertible{
+            fixed(bool* ptr = dims){
+                MeanParam p = new MeanParam() { dims = new IntPtr(ptr) };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Mean, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+            if(!keepDims){
+                dst.TLayout.RemoveAllDanglingAxisInplace();
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int[] axes){
+            var res = new TensorLayout(src as TensorShape, DType.Float64);
+            foreach(var dim in axes){
+                res.Shape[dim] = 1;
+            }
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int axis){
+            var res = new TensorLayout(src as TensorShape, DType.Float64);
+            res.Shape[axis] = 1;
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            var res = new TensorLayout(src as TensorShape, DType.Float64);
+            res.Shape.AsSpan().Fill(1);
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <Meanmary>
+        /// Get the mean of the tensor along some axes.
+        /// </Meanmary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> </param>
+        /// <param name="axes"> The axes to get mean result. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns>The Mean tensor</returns>
+        public static Tensor<double> Mean<T>(Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Mean(axes, keepDims);
+        }
+        /// <Meanmary>
+        /// Get the mean of the tensor along an axis.
+        /// </Meanmary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get mean result. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns>The Mean tensor</returns>
+        public static Tensor<double> Mean<T>(Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Mean(axis, keepDims);
+        }
+        /// <Meanmary>
+        /// Mean the tensor.
+        /// </Meanmary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns>The Mean tensor</returns>
+        public static Tensor<double> Mean<T>(Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Mean(keepDims);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Min.cs b/csharp/Tensor.NET/Statistics/Min.cs
new file mode 100644
index 0000000..e485ffa
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Min.cs
@@ -0,0 +1,126 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class MinExtension{
+        /// <summary>
+        /// Get the minimum elements along some axes.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axes"> The axes to get minimum value. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Min<T>(this Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, axes));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            foreach(var axis in axes){
+                span[axis] = true;
+            }
+            MinInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the minimum elements along an axis.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get minimum value. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Min<T>(this Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, axis));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            span[axis] = true;
+            MinInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the minimum element of a tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Min<T>(this Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            boolDims.AsSpan().Fill(true);
+            MinInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        private unsafe static void MinInternal<T>(Tensor<T> src, Tensor<T> dst, bool[] dims, bool keepDims) where T : struct, IEquatable<T>, IConvertible{
+            fixed(bool* ptr = dims){
+                MinParam p = new MinParam() { dims = new IntPtr(ptr) };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Min, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+            if(!keepDims){
+                dst.TLayout.RemoveAllDanglingAxisInplace();
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int[] axes){
+            var res = new TensorLayout(src, true);
+            foreach(var dim in axes){
+                res.Shape[dim] = 1;
+            }
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int axis){
+            var res = new TensorLayout(src, true);
+            res.Shape[axis] = 1;
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            var res = new TensorLayout(src, true);
+            res.Shape.AsSpan().Fill(1);
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <Summary>
+        /// Get the minimum elements of the tensor along some axes.
+        /// </Summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to get minimum elements. </param>
+        /// <param name="axes"> The axes to get minimum elements. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Min<T>(Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Min(axes, keepDims);
+        }
+        /// <Summary>
+        /// Get the minimum elements of the tensor along an axis.
+        /// </Summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to get minimum elements. </param>
+        /// <param name="axis"> The axis to get minimum elements. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Min<T>(Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Min(axis, keepDims);
+        }
+        /// <Summary>
+        /// Get the minimum element of the tensor.
+        /// </Summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"> The tensor to get minimum element. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Min<T>(Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Min(keepDims);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Sort.cs b/csharp/Tensor.NET/Statistics/Sort.cs
new file mode 100644
index 0000000..60371c1
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Sort.cs
@@ -0,0 +1,59 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public enum SortOrder{
+        Increase = 0,
+        Decrease = 1
+    }
+    public static class SortExtension{
+        /// <summary>
+        /// Return a sorted copy of a tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.sort.html?highlight=sort#numpy.sort
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> Axis along which to sort. By default it sorts the last axis. Please set it to zero or a positive number.</param>
+        /// <param name="order"> The order of the sort. </param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        public static Tensor<T> Sort<T>(this Tensor<T> src, int axis = -1, SortOrder order = SortOrder.Increase) where T : struct, IEquatable<T>, IConvertible
+        {
+            if(axis == -1){
+                axis = src.TLayout.NDim - 1;
+            }
+            if(axis <= -2 || axis >= src.TLayout.NDim){
+                throw new InvalidParamException("The axis to sort exceeds the range of dims");
+            }
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            SortInternal(src, res, axis, order);
+            return res;
+        }
+        private unsafe static void SortInternal<T>(Tensor<T> src, Tensor<T> dst, int axis, SortOrder order) where T : struct, IEquatable<T>, IConvertible{
+            SortParam p = new SortParam() { axis = axis, order = order };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Sort, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            return new TensorLayout(src, true);
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Return a sorted copy of a tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.sort.html?highlight=sort#numpy.sort
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> Axis along which to sort. By default it sorts the last axis. Please set it to zero or a positive number.</param>
+        /// <param name="order"> The order of the sort. </param>
+        /// <returns></returns>
+        /// <exception cref="InvalidParamException"></exception>
+        public static Tensor<T> Sort<T>(Tensor<T> src, int axis = -1, SortOrder order = SortOrder.Increase) where T : struct, IEquatable<T>, IConvertible{
+            return src.Sort(axis, order);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Statistics/Sum.cs b/csharp/Tensor.NET/Statistics/Sum.cs
new file mode 100644
index 0000000..ee37039
--- /dev/null
+++ b/csharp/Tensor.NET/Statistics/Sum.cs
@@ -0,0 +1,126 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public static class SumExtension{
+        /// <summary>
+        /// Get the sum of the elements of a tensor along some axes.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axes"> The axes to get sum. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Sum<T>(this Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, axes));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            foreach(var axis in axes){
+                span[axis] = true;
+            }
+            SumInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the sum of the elements of a tensor along an axis.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get the sum. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Sum<T>(this Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout, axis));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            var span = boolDims.AsSpan();
+            span.Fill(false);
+            span[axis] = true;
+            SumInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        /// <summary>
+        /// Get the sum of the elements of a tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Sum<T>(this Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible
+        {
+            Tensor<T> res = new Tensor<T>(DeduceLayout(src.TLayout));
+            res.TLayout.InitContiguousLayout();
+            bool[] boolDims = new bool[src.TLayout.NDim];
+            boolDims.AsSpan().Fill(true);
+            SumInternal(src, res, boolDims, keepDims);
+            return res;
+        }
+        private unsafe static void SumInternal<T>(Tensor<T> src, Tensor<T> dst, bool[] dims, bool keepDims) where T : struct, IEquatable<T>, IConvertible{
+            fixed(bool* ptr = dims){
+                SumParam p = new SumParam() { dims = new IntPtr(ptr) };
+                IntPtr status = NativeExecutor.Execute(NativeApi.Sum, src.TMemory, dst.TMemory, src.TLayout, dst.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+            if(!keepDims){
+                dst.TLayout.RemoveAllDanglingAxisInplace();
+            }
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int[] axes){
+            var res = new TensorLayout(src, true);
+            foreach(var dim in axes){
+                res.Shape[dim] = 1;
+            }
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src, int axis){
+            var res = new TensorLayout(src, true);
+            res.Shape[axis] = 1;
+            return res;
+        }
+        private static TensorLayout DeduceLayout(TensorLayout src){
+            var res = new TensorLayout(src, true);
+            res.Shape.AsSpan().Fill(1);
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Get the sum of the elements of a tensor along some axes.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axes"> The axes to get sum. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Sum<T>(Tensor<T> src, int[] axes, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Sum(axes, keepDims);
+        }
+        /// <summary>
+        /// Get the sum of the elements of a tensor along an axis.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="axis"> The axis to get the sum. </param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Sum<T>(Tensor<T> src, int axis, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Sum(axis, keepDims);
+        }
+        /// <summary>
+        /// Get the sum of the elements of a tensor.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="src"></param>
+        /// <param name="keepDims"> Whether to keep the dims all eliminate the dims. False by default. </param>
+        /// <returns></returns>
+        public static Tensor<T> Sum<T>(Tensor<T> src, bool keepDims = false) where T : struct, IEquatable<T>, IConvertible{
+            return src.Sum(keepDims);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor.NET.csproj b/csharp/Tensor.NET/Tensor.NET.csproj
new file mode 100644
index 0000000..49d3b47
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor.NET.csproj
@@ -0,0 +1,57 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <PackageId>Tensor.NET</PackageId>
+    <Version>0.1.1</Version>
+    <Authors>AsakusaRinne</Authors>
+    <Company>Null</Company>
+    <Description>
+      A lightweight and high-performance tensor library which provides numpy-like operations but .NET style interfaces. It supports generic tensor, Linq, C# native slices and so on. It is young so that it may may lack some features or have some BUGs. Please tell us on github or via email, thank you!
+
+      Github repository: https://github.com/AsakusaRinne/Tensor.NET
+
+      Email: AsakusaRinne@gmail.com
+
+      Update information:
+        1. Add static method for load and save.
+        2. Add mod, and, or, xor operators for Tensor.
+        3. Add ForEach method for Tensor.
+        4. Add docs for public APIs. 
+
+      Corresponding commit ID for this version: e1faabd35d7bb2671d6cb52fc2c6f8ef2edc43f1
+
+    </Description>
+    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
+    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <NoWarn>$(NoWarn);1591</NoWarn>
+  </PropertyGroup>
+
+  <ItemGroup>
+        <Content Include="./CppLibrary/libtensornet.dll">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+            <Pack>true</Pack>
+            <PackagePath>lib\$(TargetFramework)</PackagePath>
+        </Content>
+        <Content Include="./CppLibrary/libstdc++-6.dll">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+            <Pack>true</Pack>
+            <PackagePath>lib\$(TargetFramework)</PackagePath>
+        </Content>
+        <Content Include="./CppLibrary/libgcc_s_seh-1.dll">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+            <Pack>true</Pack>
+            <PackagePath>lib\$(TargetFramework)</PackagePath>
+        </Content>
+        <Content Include="libtensornet.so">
+            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+            <PackageCopyToOutput>true</PackageCopyToOutput>
+            <pack>true</pack>
+        </Content>
+    </ItemGroup>
+
+</Project>
diff --git a/csharp/Tensor.NET/Tensor/Common/BroadCast.cs b/csharp/Tensor.NET/Tensor/Common/BroadCast.cs
new file mode 100644
index 0000000..e32532e
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/BroadCast.cs
@@ -0,0 +1,47 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Broadcast the tensor to the given shape.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// </summary>
+        /// <param name="targetShape"> The shape to broadcast to.</param>
+        /// <returns></returns>
+        public Tensor<T> Broadcast(Span<int> targetShape){
+            return new Tensor<T>(this.TMemory, this.TLayout.Broadcast(new TensorShape(targetShape)));
+        }
+        /// <summary>
+        /// Broadcast the tensor to the given shape.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// </summary>
+        /// <param name="targetShape"> The shape to broadcast to.</param>
+        /// <returns></returns>
+        public Tensor<T> Broadcast(TensorShape targetShape){
+            return new Tensor<T>(this.TMemory, this.TLayout.Broadcast(targetShape));
+        }
+        /// <summary>
+        /// Broadcast the tensor to the given shape.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// </summary>
+        /// <param name="targetShape"> The shape to broadcast to.</param>
+        /// <returns></returns>
+        public Tensor<T> Broadcast(int[] targetShape){
+            return new Tensor<T>(this.TMemory, this.TLayout.Broadcast(new TensorShape(targetShape)));
+        }
+        /// <summary>
+        /// Broadcast the tensor to the given shape in place.
+        /// </summary>
+        /// <param name="targetShape"> The shape to broadcast to.</param>
+        public void BroadcastTo(TensorShape targetShape){
+            TLayout.BroadcastInplace(targetShape);
+        }
+        /// <summary>
+        /// Broadcast the tensor to the given shape in place.
+        /// </summary>
+        /// <param name="targetShape"> The shape to broadcast to.</param>
+        public void BroadcastTo(int[] targetShape){
+            TLayout.BroadcastInplace(new TensorShape(targetShape));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Common/DefaultTypeDeduce.cs b/csharp/Tensor.NET/Tensor/Common/DefaultTypeDeduce.cs
similarity index 88%
rename from csharp/Num.NET/Tensor/Common/DefaultTypeDeduce.cs
rename to csharp/Tensor.NET/Tensor/Common/DefaultTypeDeduce.cs
index 7c07c8e..b181d03 100644
--- a/csharp/Num.NET/Tensor/Common/DefaultTypeDeduce.cs
+++ b/csharp/Tensor.NET/Tensor/Common/DefaultTypeDeduce.cs
@@ -1,8 +1,8 @@
-using Numnet.Native;
+using Tensornet.Native;
 
-namespace Numnet.Common{
+namespace Tensornet.Common{
     internal static class DefaultTypeDeduce{
-        public static DType Deduce(DType lhs, DType rhs){
+        internal static DType Deduce(DType lhs, DType rhs){
             if(lhs == rhs) return lhs;
             if(lhs == DType.Float64 || rhs == DType.Float64){
                 return DType.Float64;
diff --git a/csharp/Tensor.NET/Tensor/Common/InplaceOperation.cs b/csharp/Tensor.NET/Tensor/Common/InplaceOperation.cs
new file mode 100644
index 0000000..b0f3c87
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/InplaceOperation.cs
@@ -0,0 +1,94 @@
+
+namespace Tensornet.Common{
+    internal static class InplaceOperation{
+        public static void Execute<T>(Tensor<T> inp, Func<T, T> operation) 
+            where T : struct, IEquatable<T>, IConvertible{
+            int idxOffset = inp.TLayout.NDim - TensorLayout.MAX_NDIM;
+            Span<T> spanInp = inp.AsSpan();
+            for (int n = 0; n < (idxOffset >= 0 ? inp.TLayout.Shape[idxOffset] : 1); n++){
+                int nOffsetInp = idxOffset >= 0 ? n * inp.TLayout.Stride[idxOffset] : 0;
+                int nOffsetinp = idxOffset >= 0 ? n * inp.TLayout.Stride[idxOffset] : 0;
+                for (int c = 0; c < (idxOffset >= -1 ? inp.TLayout.Shape[idxOffset + 1] : 1); c++)
+                {
+                    int ncOffsetInp = (idxOffset >= -1 ? c * inp.TLayout.Stride[idxOffset + 1] : 0) + nOffsetInp;
+                    int ncOffsetinp = (idxOffset >= -1 ? c * inp.TLayout.Stride[idxOffset + 1] : 0) + nOffsetinp;
+                    for (int h = 0; h < (idxOffset >= -2 ? inp.TLayout.Shape[idxOffset + 2] : 1); h++){
+                        int nchOffsetInp = (idxOffset >= -2 ? h * inp.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetInp;
+                        int nchOffsetinp = (idxOffset >= -2 ? h * inp.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetinp;
+                        for (int w = 0; w < (idxOffset >= -3 ? inp.TLayout.Shape[idxOffset + 3] : 1); w++){
+                            spanInp[nchOffsetinp + w * inp.TLayout.Stride[idxOffset + 3]] = 
+                                operation(spanInp[nchOffsetInp + w * inp.TLayout.Stride[idxOffset + 3]]);
+                        }
+                    }
+                }
+            }
+        }
+
+        public static void Execute<TInput, TRefer>(Tensor<TInput> inp, Tensor<TRefer> refer, Func<TInput, TRefer, TInput> operation) 
+            where TInput : struct, IEquatable<TInput>, IConvertible 
+            where TRefer : struct, IEquatable<TRefer>, IConvertible{
+            int idxOffset = inp.TLayout.NDim - TensorLayout.MAX_NDIM;
+            Span<TRefer> spanRefer = refer.AsSpan();
+            Span<TInput> spanInp = inp.AsSpan();
+            for (int n = 0; n < (idxOffset >= 0 ? refer.TLayout.Shape[idxOffset] : 1); n++){
+                int nOffsetInp = idxOffset >= 0 ? n * inp.TLayout.Stride[idxOffset] : 0;
+                int nOffsetRefer = idxOffset >= 0 ? n * refer.TLayout.Stride[idxOffset] : 0;
+                for (int c = 0; c < (idxOffset >= -1 ? refer.TLayout.Shape[idxOffset + 1] : 1); c++)
+                {
+                    int ncOffsetInp = (idxOffset >= -1 ? c * inp.TLayout.Stride[idxOffset + 1] : 0) + nOffsetInp;
+                    int ncOffsetRefer = (idxOffset >= -1 ? c * refer.TLayout.Stride[idxOffset + 1] : 0) + nOffsetRefer;
+                    for (int h = 0; h < (idxOffset >= -2 ? refer.TLayout.Shape[idxOffset + 2] : 1); h++){
+                        int nchOffsetInp = (idxOffset >= -2 ? h * inp.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetInp;
+                        int nchOffsetrefer = (idxOffset >= -2 ? h * refer.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetRefer;
+                        for (int w = 0; w < (idxOffset >= -3 ? refer.TLayout.Shape[idxOffset + 3] : 1); w++){
+                            spanInp[nchOffsetInp + w * inp.TLayout.Stride[idxOffset + 3]] = 
+                                operation(spanInp[nchOffsetInp + w * inp.TLayout.Stride[idxOffset + 3]], spanRefer[nchOffsetrefer + w * refer.TLayout.Stride[idxOffset + 3]]);
+                        }
+                    }
+                }
+            }
+        }
+
+        public static void Execute<T>(Tensor<T> inp, Tensor<bool> condition, Tensor<T> refer) 
+            where T : struct, IEquatable<T>, IConvertible {
+            int idxOffset = inp.TLayout.NDim - TensorLayout.MAX_NDIM;
+            Span<T> spanRefer = refer.AsSpan();
+            Span<T> spanInp = inp.AsSpan();
+            Span<bool> spanCondition = condition.AsSpan();
+            for (int n = 0; n < (idxOffset >= 0 ? refer.TLayout.Shape[idxOffset] : 1); n++){
+                int nOffsetInp = idxOffset >= 0 ? n * inp.TLayout.Stride[idxOffset] : 0;
+                int nOffsetRefer = idxOffset >= 0 ? n * refer.TLayout.Stride[idxOffset] : 0;
+                int nOffsetCondition = idxOffset >= 0 ? n * condition.TLayout.Stride[idxOffset] : 0;
+                for (int c = 0; c < (idxOffset >= -1 ? refer.TLayout.Shape[idxOffset + 1] : 1); c++)
+                {
+                    int ncOffsetInp = (idxOffset >= -1 ? c * inp.TLayout.Stride[idxOffset + 1] : 0) + nOffsetInp;
+                    int ncOffsetRefer = (idxOffset >= -1 ? c * refer.TLayout.Stride[idxOffset + 1] : 0) + nOffsetRefer;
+                    int ncOffsetCondition = (idxOffset >= -1 ? c * condition.TLayout.Stride[idxOffset + 1] : 0) + nOffsetCondition;
+                    for (int h = 0; h < (idxOffset >= -2 ? refer.TLayout.Shape[idxOffset + 2] : 1); h++){
+                        int nchOffsetInp = (idxOffset >= -2 ? h * inp.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetInp;
+                        int nchOffsetrefer = (idxOffset >= -2 ? h * refer.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetRefer;
+                        int nchOffsetCondition = (idxOffset >= -2 ? h * condition.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetCondition;
+                        for (int w = 0; w < (idxOffset >= -3 ? refer.TLayout.Shape[idxOffset + 3] : 1); w++){
+                            if(spanCondition[nchOffsetCondition + w * condition.TLayout.Stride[idxOffset + 3]]){
+                                spanInp[nchOffsetInp + w * inp.TLayout.Stride[idxOffset + 3]] = spanRefer[nchOffsetrefer + w * refer.TLayout.Stride[idxOffset + 3]];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Apply an function on each element of the current tensor inplace.
+        /// Note that the modification will be made on the current tensor directly.
+        /// </summary>
+        /// <param name="operation"></param>
+        public void ForEachInplace(Func<T, T> operation){
+            Common.InplaceOperation.Execute(this, operation);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Common/InterElemOperation.cs b/csharp/Tensor.NET/Tensor/Common/InterElemOperation.cs
new file mode 100644
index 0000000..3ff4433
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/InterElemOperation.cs
@@ -0,0 +1,146 @@
+using Tensornet.Exceptions;
+using Tensornet.Native;
+using Tensornet.Native.Param;
+
+namespace Tensornet.Common{
+    internal enum InterElemOperationType{
+        Add = 1,
+        Sub = 2,
+        Mul = 3,
+        Div = 4,
+        Mod = 5,
+        And = 6,
+        Or = 7,
+        Xor = 8
+    }
+    internal static class InterElemOperation{
+        public static unsafe Tensor<T> Execute<T>(Tensor<T> a, Tensor<T> b, InterElemOperationType operationType) 
+            where T : struct, IEquatable<T>, IConvertible{
+            TensorLayout resLayout = new TensorLayout();
+            resLayout.DType = TensorTypeInfo.GetTypeInfo(typeof(T))._dtype;
+            resLayout.NDim = System.Math.Max(a.TLayout.NDim, b.TLayout.NDim);
+            for (int i = a.TLayout.NDim - 1, j = b.TLayout.NDim - 1, idx = resLayout.NDim - 1; i >= 0 || j >= 0; i--, j--, idx--){
+                if(i < 0){
+                    resLayout.Shape[idx] = b.TLayout.Shape[j];
+                }
+                else if(j < 0){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else if(a.TLayout.Shape[i] == b.TLayout.Shape[j]){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else if(a.TLayout.Shape[i] == 1){
+                    resLayout.Shape[idx] = b.TLayout.Shape[j];
+                }
+                else if(b.TLayout.Shape[j] == 1){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else{
+                    throw new MismatchedShapeException($"Cannot broadcast between the shape {a.TLayout as TensorShape} and shape {b.TLayout as TensorShape}.");
+                }
+            }
+            resLayout.InitContiguousLayout();
+            Tensor<T> tempA = a.Broadcast(resLayout);
+            Tensor<T> tempB = b.Broadcast(resLayout);
+            Tensor<T> res = new Tensor<T>(resLayout);
+            InterelemParam p = new InterelemParam() { operationType = operationType };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Interelem, tempA.TMemory, tempB.TMemory, res.TMemory, tempA.TLayout, tempB.TLayout, res.TLayout, new IntPtr(&p), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+            return res;
+        }
+        public static unsafe Tensor<TResult> Execute<TA, TB, TResult>(Tensor<TA> a, Tensor<TB> b, InterElemOperationType operationType) 
+            where TA : struct, IEquatable<TA>, IConvertible 
+            where TB : struct, IEquatable<TB>, IConvertible 
+            where TResult : struct, IEquatable<TResult>, IConvertible{
+            TensorLayout resLayout = new TensorLayout();
+            resLayout.DType = TensorTypeInfo.GetTypeInfo(typeof(TResult))._dtype;
+            resLayout.NDim = System.Math.Max(a.TLayout.NDim, b.TLayout.NDim);
+            for (int i = a.TLayout.NDim - 1, j = b.TLayout.NDim - 1, idx = resLayout.NDim - 1; i >= 0 || j >= 0; i--, j--, idx--){
+                if(i < 0){
+                    resLayout.Shape[idx] = b.TLayout.Shape[j];
+                }
+                else if(j < 0){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else if(a.TLayout.Shape[i] == b.TLayout.Shape[j]){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else if(a.TLayout.Shape[i] == 1){
+                    resLayout.Shape[idx] = b.TLayout.Shape[j];
+                }
+                else if(b.TLayout.Shape[j] == 1){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else{
+                    throw new MismatchedShapeException($"Cannot broadcast between the shape {a.TLayout as TensorShape} and shape {b.TLayout as TensorShape}.");
+                }
+            }
+            resLayout.InitContiguousLayout();
+            Tensor<TA> tempA = a.Broadcast(resLayout);
+            Tensor<TB> tempB = b.Broadcast(resLayout);
+            Tensor<TResult> res = new Tensor<TResult>(resLayout);
+            InterelemParam p = new InterelemParam() { operationType = operationType };
+            IntPtr status = NativeExecutor.Execute(NativeApi.Interelem, tempA.TMemory, tempB.TMemory, res.TMemory, tempA.TLayout, tempB.TLayout, res.TLayout, new IntPtr(&p), Tensor<TResult>.Provider);
+            NativeStatus.AssertOK(status);
+            return res;
+        }
+        public static Tensor<TResult> Execute<TA, TB, TResult>(Tensor<TA> a, Tensor<TB> b, Func<TA, TB, TResult> operation) 
+            where TA : struct, IEquatable<TA>, IConvertible 
+            where TB : struct, IEquatable<TB>, IConvertible 
+            where TResult : struct, IEquatable<TResult>, IConvertible{
+            TensorLayout resLayout = new TensorLayout();
+            resLayout.DType = TensorTypeInfo.GetTypeInfo(typeof(TResult))._dtype;
+            resLayout.NDim = System.Math.Max(a.TLayout.NDim, b.TLayout.NDim);
+            for (int i = a.TLayout.NDim - 1, j = b.TLayout.NDim - 1, idx = resLayout.NDim - 1; i >= 0 || j >= 0; i--, j--, idx--){
+                if(i < 0){
+                    resLayout.Shape[idx] = b.TLayout.Shape[j];
+                }
+                else if(j < 0){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else if(a.TLayout.Shape[i] == b.TLayout.Shape[j]){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else if(a.TLayout.Shape[i] == 1){
+                    resLayout.Shape[idx] = b.TLayout.Shape[j];
+                }
+                else if(b.TLayout.Shape[j] == 1){
+                    resLayout.Shape[idx] = a.TLayout.Shape[i];
+                }
+                else{
+                    throw new MismatchedShapeException($"Cannot broadcast between the shape {a.TLayout as TensorShape} and shape {b.TLayout as TensorShape}.");
+                }
+            }
+            resLayout.InitContiguousLayout();
+            Tensor<TA> tempA = a.Broadcast(resLayout);
+            Tensor<TB> tempB = b.Broadcast(resLayout);
+            Tensor<TResult> res = new Tensor<TResult>(resLayout);
+            int idxOffset = resLayout.NDim - TensorLayout.MAX_NDIM;
+            Span<TResult> spanRes = res.AsSpan();
+            Span<TA> spanA = a.AsSpan();
+            Span<TB> spanB = b.AsSpan();
+            for (int n = 0; n < (idxOffset >= 0 ? resLayout.Shape[idxOffset] : 1); n++){
+                int nOffsetA = idxOffset >= 0 ? n * tempA.TLayout.Stride[idxOffset] : 0;
+                int nOffsetB = idxOffset >= 0 ? n * tempB.TLayout.Stride[idxOffset] : 0;
+                int nOffsetRes = idxOffset >= 0 ? n * resLayout.Stride[idxOffset] : 0;
+                for (int c = 0; c < (idxOffset >= -1 ? resLayout.Shape[idxOffset + 1] : 1); c++)
+                {
+                    int ncOffsetA = (idxOffset >= -1 ? c * tempA.TLayout.Stride[idxOffset + 1] : 0) + nOffsetA;
+                    int ncOffsetB = (idxOffset >= -1 ? c * tempB.TLayout.Stride[idxOffset + 1] : 0) + nOffsetB;
+                    int ncOffsetRes = (idxOffset >= -1 ? c * resLayout.Stride[idxOffset + 1] : 0) + nOffsetRes;
+                    for (int h = 0; h < (idxOffset >= -2 ? resLayout.Shape[idxOffset + 2] : 1); h++){
+                        int nchOffsetA = (idxOffset >= -2 ? h * tempA.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetA;
+                        int nchOffsetB = (idxOffset >= -2 ? h * tempB.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetB;
+                        int nchOffsetRes = (idxOffset >= -2 ? h * resLayout.Stride[idxOffset + 2] : 0) + ncOffsetRes;
+                        for (int w = 0; w < (idxOffset >= -3 ? resLayout.Shape[idxOffset + 3] : 1); w++){
+                            spanRes[nchOffsetRes + w * resLayout.Stride[idxOffset + 3]] = 
+                                operation(spanA[nchOffsetA + w * tempA.TLayout.Stride[idxOffset + 3]], 
+                                          spanB[nchOffsetB + w * tempB.TLayout.Stride[idxOffset + 3]]);
+                        }
+                    }
+                }
+            }
+            return res;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Common/OnElemOperation.cs b/csharp/Tensor.NET/Tensor/Common/OnElemOperation.cs
new file mode 100644
index 0000000..d81e5c4
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/OnElemOperation.cs
@@ -0,0 +1,46 @@
+
+namespace Tensornet.Common{
+    internal static class OnElemOperation{
+        public static Tensor<TResult> Execute<TInput, TResult>(Tensor<TInput> inp, Func<TInput, TResult> operation) 
+            where TInput : struct, IEquatable<TInput>, IConvertible 
+            where TResult : struct, IEquatable<TResult>, IConvertible{
+            Tensor<TResult> res = new Tensor<TResult>(new TensorLayout(inp.TLayout, TensorTypeInfo.GetTypeInfo(typeof(TResult))._dtype));
+            int idxOffset = res.TLayout.NDim - TensorLayout.MAX_NDIM;
+            Span<TResult> spanRes = res.AsSpan();
+            Span<TInput> spanInp = inp.AsSpan();
+            for (int n = 0; n < (idxOffset >= 0 ? res.TLayout.Shape[idxOffset] : 1); n++){
+                int nOffsetInp = idxOffset >= 0 ? n * inp.TLayout.Stride[idxOffset] : 0;
+                int nOffsetRes = idxOffset >= 0 ? n * res.TLayout.Stride[idxOffset] : 0;
+                for (int c = 0; c < (idxOffset >= -1 ? res.TLayout.Shape[idxOffset + 1] : 1); c++)
+                {
+                    int ncOffsetInp = (idxOffset >= -1 ? c * inp.TLayout.Stride[idxOffset + 1] : 0) + nOffsetInp;
+                    int ncOffsetRes = (idxOffset >= -1 ? c * res.TLayout.Stride[idxOffset + 1] : 0) + nOffsetRes;
+                    for (int h = 0; h < (idxOffset >= -2 ? res.TLayout.Shape[idxOffset + 2] : 1); h++){
+                        int nchOffsetInp = (idxOffset >= -2 ? h * inp.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetInp;
+                        int nchOffsetRes = (idxOffset >= -2 ? h * res.TLayout.Stride[idxOffset + 2] : 0) + ncOffsetRes;
+                        for (int w = 0; w < (idxOffset >= -3 ? res.TLayout.Shape[idxOffset + 3] : 1); w++){
+                            spanRes[nchOffsetRes + w * res.TLayout.Stride[idxOffset + 3]] = 
+                                operation(spanInp[nchOffsetInp + w * inp.TLayout.Stride[idxOffset + 3]]);
+                        }
+                    }
+                }
+            }
+            return res;
+        }
+    }
+}
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Apply a function to each element of the current tensor.
+        /// Note that a new tensor will be created. If you want to directly modify the tensor, please use "ForEachInplace"
+        /// </summary>
+        /// <typeparam name="TResult"></typeparam>
+        /// <param name="operation"></param>
+        /// <returns></returns>
+        public Tensor<TResult> ForEach<TResult>(Func<T, TResult> operation) where TResult : struct, IEquatable<TResult>, IConvertible{
+            return Common.OnElemOperation.Execute(this, operation);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Common/Reshape.cs b/csharp/Tensor.NET/Tensor/Common/Reshape.cs
new file mode 100644
index 0000000..f9d5226
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/Reshape.cs
@@ -0,0 +1,55 @@
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Reshape the tensor to the given shape.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.reshape.html?highlight=reshape#numpy.reshape
+        /// </summary>
+        /// <param name="shape"></param>
+        /// <returns></returns>
+        public Tensor<T> Reshape(Span<int> shape){
+            return new Tensor<T>(this.TMemory, this.TLayout.Reshape(new TensorShape(shape), false));
+        }
+        /// <summary>
+        /// Reshape the tensor to the given shape.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.reshape.html?highlight=reshape#numpy.reshape
+        /// </summary>
+        /// <param name="shape"></param>
+        /// <returns></returns>
+        public Tensor<T> Reshape(int[] shape){
+            return new Tensor<T>(this.TMemory, this.TLayout.Reshape(new TensorShape(shape), false));
+        }
+        /// <summary>
+        /// Reshape the tensor to the given shape.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.reshape.html?highlight=reshape#numpy.reshape
+        /// </summary>
+        /// <param name="shape"></param>
+        /// <returns></returns>
+        public Tensor<T> Reshape(TensorShape shape){
+            return new Tensor<T>(this.TMemory, this.TLayout.Reshape(shape, false));
+        }
+
+        /// <summary>
+        /// Reshape the tensor to the given shape in place.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.reshape.html?highlight=reshape#numpy.reshape
+        /// </summary>
+        /// <param name="shape"></param>
+        public void ReshapeTo(int[] shape){
+            TLayout = TLayout.Reshape(new TensorShape(shape), false);
+        }
+        /// <summary>
+        /// Reshape the tensor to the given shape in place.
+        /// Please note that the returned tensor shares the memory with the source tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.reshape.html?highlight=reshape#numpy.reshape
+        /// </summary>
+        /// <param name="shape"></param>
+        public void ReshapeTo(TensorShape shape){
+            TLayout = TLayout.Reshape(shape, false);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Common/Slice.cs b/csharp/Tensor.NET/Tensor/Common/Slice.cs
new file mode 100644
index 0000000..a782513
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/Slice.cs
@@ -0,0 +1,143 @@
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    public struct Slice{
+        public int Begin{ get; internal set; }
+        public int End{ get; internal set; }
+        public int Step{ get; internal set; }
+        public Slice(int dim){
+            Begin = dim;
+            End = dim;
+            Step = 0;
+        }
+        public Slice(int begin, int end, int step = 1){
+            Begin = begin;
+            End = end;
+            Step = step;
+        }
+        public Slice(Range range){
+            Begin = range.Start.IsFromEnd? -range.Start.Value : range.Start.Value;
+            End = range.End.IsFromEnd ? -range.End.Value : range.End.Value;
+            if(Begin == 0 && range.Start.IsFromEnd){
+                Step = 1;
+                return;
+            }
+            if(End == 0 && range.End.IsFromEnd){
+                End = Begin - 1;
+                Step = -1;
+                return;
+            }
+            Step = 1;
+        }
+        public Slice(Index index){
+            Begin = index.IsFromEnd ? -index.Value : index.Value;
+            End = Begin;
+            Step = 0;
+        }
+        public static implicit operator Slice(Range range){
+            return new Slice(range);
+        }
+        public static implicit operator Slice(Index index){
+            return new Slice(index);
+        }
+        public static implicit operator Slice(int index){
+            return new Slice(index);
+        }
+    }
+
+    public sealed partial class TensorLayout{
+        internal TensorLayout ApplySlice(Slice s, int axis){
+            TensorLayout res = new TensorLayout(this);
+            if(axis < 0) axis += res.NDim;
+            if(axis < 0){
+                throw new InvalidArgumentException($"Invalid axis to slice.");
+            }
+            if(axis >= res.NDim){
+                throw new InvalidArgumentException($"Axis to slice exceeds the limit. The axis is {axis}, the limit is {res.NDim - 1}.");
+            }
+            if(s.End + 1 == s.Begin && s.Step == -1){
+                s.End = Shape[axis];
+                s.Step = 1;
+            }
+            int axisDim = res.Shape[axis];
+            int begin = s.Begin;
+            int end = s.End;
+            int step = s.Step;
+            if(step < 0){
+                throw new InvalidArgumentException($"The step of slice cannot be negative.");
+            }
+            
+            if(begin < 0) begin += axisDim;
+            if(end < 0) end += axisDim;
+            if(end < begin){
+                throw new InvalidArgumentException($"The end index of slice should be larger than the begin index. But the begin is {begin}, the end is {end}.");
+            }
+            // is not scalar
+            if(step > 0){
+                res.Shape[axis] = (end - begin + step - 1) / step;
+                res.Offset += Stride[axis] * begin;
+                res.Stride[axis] *= step;
+            }
+            // If it's a scalar, the begin must be equal to end, and the step = 0.
+            else if(begin == end){
+                res.Shape[axis] = 1;
+                res.Offset += Stride[axis] * begin;
+            }
+            else{
+                throw new InvalidArgumentException($"Bad slice. It may be caused by setting step = 0 but begin != end.");
+            }
+
+            return res;
+        }
+    }
+
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Get the slice of the tensor.
+        /// Please note that except of the Slice class in Tensor.NET, Range and Index in C# 8 or higher is also supported. For details, please refer to https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/ranges
+        /// </summary>
+        /// <param name="slices"></param>
+        /// <returns></returns>
+        /// <exception cref="InvalidArgumentException"></exception>
+        public Tensor<T> this[params Slice[] slices]{
+            get{
+                if(slices.Length > TLayout.NDim){
+                    throw new InvalidArgumentException("Too many dims of slice.");
+                }
+                else if(slices.Length < TLayout.NDim){
+                    throw new InvalidArgumentException("Too few dims of slice.");
+                }
+                TensorLayout layout = new TensorLayout(TLayout);
+                for (int i = 0; i < slices.Length; i++){
+                    layout = layout.ApplySlice(slices[i], i);
+                }
+                for (int i = slices.Length - 1; i >= 0; i--){
+                    if(slices[i].Step == 0 && slices[i].Begin == slices[i].End){
+                        layout.RemoveAxisInplace(i);
+                    }
+                }
+                return new Tensor<T>(TMemory, layout);
+            }
+        }
+        public Tensor<T> this[params Range[] slices]{
+            get{
+                if(slices.Length > TLayout.NDim){
+                    throw new InvalidArgumentException("Too many dims of slice.");
+                }
+                else if(slices.Length < TLayout.NDim){
+                    throw new InvalidArgumentException("Too few dims of slice.");
+                }
+                TensorLayout layout = new TensorLayout(TLayout);
+                for (int i = 0; i < slices.Length; i++){
+                    layout = layout.ApplySlice(new Slice(slices[i]), i);
+                }
+                for (int i = slices.Length - 1; i >= 0; i--){
+                    if(slices[i].Start.Value == slices[i].End.Value && slices[i].Start.IsFromEnd == slices[i].End.IsFromEnd){
+                        layout.RemoveAxisInplace(i);
+                    }
+                }
+                return new Tensor<T>(TMemory, layout);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Common/Squeeze.cs b/csharp/Tensor.NET/Tensor/Common/Squeeze.cs
new file mode 100644
index 0000000..4244a37
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/Squeeze.cs
@@ -0,0 +1,32 @@
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Squeeze the tensor to remove the dim which equals to one.
+        /// </summary>
+        /// <param name="axis"> The axis to be removed. If it's set to -1, then all dims of length one will be removed. </param>
+        /// <returns></returns>
+        public Tensor<T> Squeeze(int axis = -1){
+            if(axis < -1){
+                throw new InvalidArgumentException("The axis to be squeezed should not be smaller than -1.");
+            }
+            if(axis >= TLayout.NDim){
+                throw new InvalidArgumentException("The axis to be squeezed is too large.");
+            }
+            var layout = new TensorLayout(this.TLayout);
+            if(axis == -1){
+                for (int i = TLayout.NDim - 1; i >= 0 && layout.NDim >= 2; i--){
+                    if(layout.Shape[i] == 1) layout.RemoveAxisInplace(i);
+                }
+            }
+            else if(TLayout.Shape[axis] == 1){
+                layout.RemoveAxisInplace(axis);
+            }
+            else{
+                throw new InvalidArgumentException("The axis to be squeezed does not have value 1.");
+            }
+            return new Tensor<T>(this.TMemory, layout);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Common/TensorLayout.cs b/csharp/Tensor.NET/Tensor/Common/TensorLayout.cs
similarity index 51%
rename from csharp/Num.NET/Tensor/Common/TensorLayout.cs
rename to csharp/Tensor.NET/Tensor/Common/TensorLayout.cs
index 6ffc6c5..2fa54d5 100644
--- a/csharp/Num.NET/Tensor/Common/TensorLayout.cs
+++ b/csharp/Tensor.NET/Tensor/Common/TensorLayout.cs
@@ -1,8 +1,8 @@
-using Numnet.Native;
+using Tensornet.Native;
 using System.Text;
-using Numnet.Exceptions;
+using Tensornet.Exceptions;
 
-namespace Numnet.Common{
+namespace Tensornet{
     public class TensorShape{
         public static readonly int MAX_NDIM = 4;
         public int[] Shape { get; internal set; } = new int[MAX_NDIM];
@@ -13,7 +13,7 @@ public TensorShape(params int[] shape){
             }
             NDim = shape.Length;
             for (int i = 0; i < NDim; i++){
-                Shape[i] = shape[NDim - i - 1];
+                Shape[i] = shape[i];
             }
         }
         public TensorShape(Span<int> shape){
@@ -22,7 +22,7 @@ public TensorShape(Span<int> shape){
             }
             NDim = shape.Length;
             for (int i = 0; i < NDim; i++){
-                Shape[i] = shape[NDim - i - 1];
+                Shape[i] = shape[i];
             }
         }
         public TensorShape(IEnumerable<int> shape){
@@ -30,10 +30,10 @@ public TensorShape(IEnumerable<int> shape){
                 throw new DimExceedException(shape.Count());
             }
             NDim = shape.Count();
-            int i = NDim - 1;
+            int i = 0;
             foreach(var value in shape){
                 Shape[i] = value;
-                i--;
+                i++;
             }
         }
         public TensorShape(TensorShape rhs){
@@ -46,6 +46,13 @@ internal TensorShape(){
         public bool IsScalar(){
             return NDim == 1 && Shape[0] == 1;
         }
+        public bool IsSameShape(TensorShape rhs){
+            if(NDim != rhs.NDim) return false;
+            for (int i = 0; i < NDim; i++){
+                if(Shape[i] != rhs.Shape[i]) return false;
+            }
+            return true;
+        }
         public int TotalElemCount(){
             if(NDim == 0){
                 return 0;
@@ -56,14 +63,42 @@ public int TotalElemCount(){
             }
             return res;
         }
+
+        public static implicit operator TensorShape(int[] shape){
+            return new TensorShape(shape);
+        }
+
+        public static implicit operator TensorShape(Span<int> shape){
+            return new TensorShape(shape);
+        }
+
         public override string ToString()
         {
-            return base.ToString();
+            return $"TensorShape({GetInfoString()})";
+        }
+
+        internal string GetInfoString(){
+            StringBuilder r = new StringBuilder();
+            if (NDim == 0) {
+                r.Append(" Scalar");
+            } else {
+                r.Append("shape = {");
+                for (int i = 0; i < NDim; i++) {
+                    r.Append(Shape[i]);
+                    if (i != NDim - 1) r.Append(", ");
+                }
+                r.Append("}");
+            }
+            return r.ToString();
         }
     }
-    public sealed class TensorLayout:TensorShape
+    public sealed partial class TensorLayout:TensorShape
     {
         public DType DType { get; internal set; }
+        /// <summary>
+        /// The offset here is by mean of element, while the offset in c++ means the offset of bytes.
+        /// So when calling native apis, a conversion needs to be done to interop.
+        /// </summary>
         public int Offset{ get; internal set; }
         public int[] Stride { get; internal set; } = new int[MAX_NDIM];
         public TensorLayout()
@@ -93,12 +128,24 @@ public TensorLayout(TensorShape shape, DType dtype):base(shape)
             InitContiguousLayout();
         }
 
-        public TensorLayout(TensorLayout rhs){
+        public TensorLayout(TensorLayout rhs, bool initContiguous = false){
             DType = rhs.DType;
             Offset = rhs.Offset;
             NDim = rhs.NDim;
             rhs.Shape.CopyTo(Shape.AsSpan());
-            rhs.Stride.CopyTo(Stride.AsSpan());
+            if(!initContiguous){
+                rhs.Stride.CopyTo(Stride.AsSpan());
+            }
+            else{
+                InitContiguousLayout();
+            }
+        }
+
+        public bool IsSameLayout(TensorLayout rhs){
+            for (int i = 0; i < NDim; i++){
+                if(Stride[i] != rhs.Stride[i]) return false;
+            }
+            return DType == rhs.DType && IsSameShape(rhs);
         }
 
         public override string ToString()
@@ -106,14 +153,14 @@ public override string ToString()
             return $"TensorLayout({GetInfoString()})";
         }
 
-        internal string GetInfoString(){
+        internal new string GetInfoString(){
             StringBuilder r = new StringBuilder();
             if (NDim == 0) {
                 r.Append(" Scalar");
             } else {
                 r.Append("shape = {");
                 for (int i = 0; i < NDim; i++) {
-                    r.Append(Shape[NDim - 1 - i]);
+                    r.Append(Shape[i]);
                     if (i != NDim - 1) r.Append(", ");
                 }
                 r.Append("}");
@@ -128,27 +175,30 @@ internal void InitContiguousLayout()
             int s = 1;
             for (int i = 0; i < NDim; i++)
             {
-                Stride[i] = s;
-                s *= Shape[i];
+                Stride[NDim - i - 1] = s;
+                s *= Shape[NDim - i - 1];
+            }
+        }
+
+        public bool IsContiguous(){
+            int expected = 1;
+            for (int i = 0; i < NDim; ++i) {
+                if (Shape[NDim - i - 1] != 1 && Stride[NDim - i - 1] != expected)
+                    return false;
+                expected *= Shape[NDim - i - 1];
             }
+            // empty tensors are not contiguous
+            return expected != 0;
         }
+
         internal TensorLayout Reshape(TensorShape targetShape, bool isImage){
             int targetNDim = targetShape.NDim;
             if(targetNDim <= 0){
                 throw new InvalidShapeException(targetShape.Shape, "Reshape");
             }
-            // bool isEmptyShape = false;
-            int targetTotalElems = 1;
-            for (int i = 0; i < targetNDim; ++i) {
-                // if (targetShape[i] <= 0) {
-                //     isEmptyShape = true;
-                //     break;
-                // }
-                targetTotalElems *= targetShape.Shape[i];
-            }
 
-            if(this.TotalElemCount() != targetTotalElems){
-                throw new InvalidShapeException($"Number of elements does not match in reshape: src = {this.TotalElemCount()}, dst = {targetTotalElems}.");
+            if(this.TotalElemCount() != targetShape.TotalElemCount()){
+                throw new InvalidShapeException($"Number of elements does not match in reshape: src = {this.TotalElemCount()}, dst = {targetShape.TotalElemCount()}.");
             }
 
             TensorLayout res = new TensorLayout(targetShape, this.DType);
@@ -161,44 +211,45 @@ internal TensorLayout Reshape(TensorShape targetShape, bool isImage){
                 else if(targetNDim < 2){
                     throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) is not an invalid shape for image.");
                 }
-                else if(this.Shape[0] != targetShape.Shape[1] || this.Shape[1] != targetShape.Shape[0]){
+                else if(this.Shape[NDim - 1] != targetShape.Shape[targetNDim - 2] || this.Shape[NDim - 2] != targetShape.Shape[targetNDim - 1]){
                     throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) as an image.");
                 }
-                for (int i = 2; i < this.NDim; i++) {
-                    if (targetNDim > i && targetShape.Shape[i] != this.Shape[i]) {
+                else if(NDim != targetNDim){
+                    throw new NotImplementedException("Cuurently only the reverse of width and height of image is supported.");
+                }
+                for (int i = 0; i < this.NDim - 2; i++) {
+                    if (targetShape.Shape[i] != this.Shape[i]) {
                         throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) as an image to reshape.");
                     }
                 }
-                (res.Stride[0], res.Stride[1]) = (res.Stride[1], res.Stride[0]);
+                (res.Stride[NDim - 1], res.Stride[NDim - 2]) = (res.Stride[NDim - 2], res.Stride[NDim - 1]);
                 return res;
             }
 
-            // if (isEmptyShape) {
-            //     return res;
-            // }
-            // var cont = CollapseContiguous();
-
-            // int sdim = 0, prod = 1, cont_sdim = 0;
-            // for (int i = targetNDim; i >= 0; i--) {
-            //     if(cont_sdim >= cont.NDim){
-            //         throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) to reshape.");
-            //     }
-            //     prod *= res.Shape[i];
-            //     if (prod > cont.Shape[cont_sdim])
-            //         throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) to reshape.");
-
-            //     if (prod == cont.Shape[cont_sdim] &&
-            //         (i + 1 >= targetNDim || targetShape[i + 1] != 1)) {
-            //         int s = cont.Stride[cont_sdim];
-            //         for (int j = i; j >= sdim; j--) {
-            //             res.Stride[j] = s;
-            //             s *= res.Shape[j];
-            //         }
-            //         cont_sdim++;
-            //         sdim = i + 1;
-            //         prod = 1;
-            //     }
-            // }
+            var cont = CollapseContiguous();
+
+            int sdim = 0, prod = 1, cont_sdim = 0;
+            for (int i = 0; i < targetNDim; i++) {
+                if(cont_sdim >= cont.NDim){
+                    throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) to reshape.");
+                }
+                prod *= res.Shape[i];
+                if (prod > cont.Shape[cont_sdim])
+                    throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) to reshape.");
+
+                if (prod == cont.Shape[cont_sdim] &&
+                    (i + 1 >= targetNDim || targetShape.Shape[i + 1] != 1)) {
+                    int s = cont.Stride[cont_sdim];
+                    for (int j = i; j >= sdim; j--) {
+                        res.Stride[j] = s;
+                        s *= res.Shape[j];
+                    }
+                    cont_sdim++;
+                    sdim = i + 1;
+                    prod = 1;
+                }
+            }
+            if(cont_sdim != cont.NDim) throw new InvalidShapeException($"The target shape({string.Join(',', targetShape)}) does not match the current shape({string.Join(',', this.Shape)}) to reshape.");
 
             return res;
         }
@@ -210,8 +261,8 @@ internal TensorLayout CollapseContiguous() {
             TensorLayout res = new TensorLayout(this);
 
             // remove all dims with shape 1
-            for (int i = 0; i <= res.NDim - 1 && res.NDim >= 2; ++i) {
-                if (res.Shape[i] <= 0) {
+            for (int i = 0; i <= res.NDim - 1 && res.NDim >= 2; i++) {
+                if (res.Shape[i] == 0) {
                     // empty tensor
                     res.NDim = 1;
                     res.Shape[0] = 0;
@@ -223,8 +274,6 @@ internal TensorLayout CollapseContiguous() {
 
             if (res.NDim == 1) {
                 if (res.Shape[0] <= 1) {
-                    // make it the "most canonical" contiguous layout for scalars or
-                    // empty tensors
                     res.Stride[0] = 1;
                 }
                 return res;
@@ -233,14 +282,14 @@ internal TensorLayout CollapseContiguous() {
             if(res.NDim <= 0 || res.Shape[res.NDim - 1] <= 0){
                 throw new InvalidShapeException(this.Shape, "CollapseContiguous");
             }
-            for (int i = 1; i <= res.NDim - 1; i++) {
+            for (int i = res.NDim - 2; i >= 0; i--) {
                 if(res.Shape[i] <= 0){
                     throw new InvalidShapeException(this.Shape, "CollapseContiguous");
                 }
-                if (res.Stride[i] == res.Stride[i - 1] * res.Shape[i - 1]) {
-                    res.Shape[i] *= res.Shape[i - 1];
-                    res.Stride[i] = res.Stride[i - 1];
-                    res.RemoveAxisInplace(i - 1);
+                if (res.Stride[i] == res.Stride[i + 1] * res.Shape[i + 1]) {
+                    res.Shape[i] *= res.Shape[i + 1];
+                    res.Stride[i] = res.Stride[i + 1];
+                    res.RemoveAxisInplace(i + 1);
                 }
             }
             return res;
@@ -250,16 +299,60 @@ internal void RemoveAxisInplace(int axis) {
             if(NDim < 2){
                 throw new InvalidShapeException($"Could not remove axis of a tensor with only {NDim} dims.");
             }
-            else if(axis < NDim){
+            else if(axis >= NDim){
                 throw new InvalidArgumentException($"Axis to remove exceeds the NDim. Axis is {axis}, NDim is {NDim}.");
             }
             NDim--;
-            for (int i = axis; i < NDim; ++i) {
+            for (int i = axis; i < NDim; i++) {
                 Shape[i] = Shape[i + 1];
                 Stride[i] = Stride[i + 1];
             }
         }
 
+        internal TensorLayout RemoveAxis(int axis){
+            var res = new TensorLayout(this);
+            res.RemoveAxisInplace(axis);
+            return res;
+        }
+
+        internal void RemoveAllDanglingAxisInplace(){
+            for (int i = NDim - 1; i >= 0; i--){
+                if(Shape[i] == 1 && NDim > 1){
+                    RemoveAxisInplace(i);
+                }
+            }
+        }
+
+        internal void AddAxisInplace(int axis, int shape, int stride) {
+            if(NDim + 1 > MAX_NDIM){
+                throw new InvalidArgumentException($"can not add axis at {axis} (current ndim is {NDim}, MAX_NDIM is {MAX_NDIM})");
+            }
+            NDim++;
+            for (int i = NDim- 1; i > axis; i--) {
+                Shape[i] = Shape[i - 1];
+                Stride[i] = Stride[i - 1];
+            }
+            Shape[axis] = shape;
+            Stride[axis] = stride;
+        }
+
+        internal TensorLayout AddAxis(int axis, int shape, int stride){
+            var res = new TensorLayout(this);
+            res.AddAxisInplace(axis, shape, stride);
+            return res;
+        }
+
+        internal bool CanBroadCastTo(TensorShape targetShape){
+            int targetNDim = targetShape.NDim;
+            if(targetNDim < NDim) return false;
+            for (int i = 0; i < targetNDim; i++) {
+                int cur_shape = i < NDim ? Shape[NDim - i - 1] : 1, cur_stride = i < NDim ? Stride[NDim - i - 1] : 0;
+                if (targetShape.Shape[targetNDim - i - 1] != cur_shape && cur_shape != 1 && cur_stride != 0)
+                    return false;
+            }
+            return true;
+        }
+
         internal void BroadcastInplace(TensorShape targetShape){
             int targetNDim = targetShape.NDim;
             if(NDim <= 0 || targetNDim <= 0){
@@ -270,28 +363,27 @@ internal void BroadcastInplace(TensorShape targetShape){
                 NDim = targetNDim;
                 for (int i = 0; i < targetNDim; i++) {
                     Shape[i] = targetShape.Shape[i];
-                    Stride[i] = targetShape.Shape[i] == 1?targetShape.Shape[i]:0;
+                    Stride[i] = targetShape.Shape[i] == 1?1:0;
                 }
                 return;
             }
 
             if(targetNDim < NDim){
-                throw new InvalidShapeException($"Dimension after broadcast is less than that before braodcast. " +
-                        $"The src shape is ({string.Join(',', Shape.AsSpan(0, NDim).ToArray())}), dst shape is ({string.Join(',', targetShape)}).");
+                throw new InvalidShapeException($"Dimension after broadcast is less than that before braodcast. ");
             }
 
-            for (int i = 0; i < targetNDim; ++i) {
-                int cur_shape = (i < NDim ? Shape[i] : 1), cur_stride = (i < NDim ? Stride[i] : 0);
-                if (targetShape.Shape[i] != cur_shape) {
+            for (int i = 0; i < targetNDim; i++) {
+                int targetIdx = targetNDim - i - 1;
+                int cur_shape = i < NDim ? Shape[NDim - i - 1] : 1, cur_stride = i < NDim ? Stride[NDim - i - 1] : 0;
+                if (targetShape.Shape[targetIdx] != cur_shape) {
                     if(cur_shape != 1 && cur_stride != 0){
-                        throw new InvalidShapeException("Broadcast on dim with shape not equal to 1: " + 
-                            $"src_shape=({string.Join(',', Shape.AsSpan(0, NDim).ToArray())}) dst_shape=({string.Join(',', targetShape)})");
+                        throw new InvalidShapeException($"Broadcast on dim {NDim - i - 1} with shape not equal to 0 or 1.");
                     }
-                    Shape[i] = targetShape.Shape[i];
-                    Stride[i] = 0;
+                    Shape[targetIdx] = targetShape.Shape[targetIdx];
+                    Stride[targetIdx] = 0;
                 } else {
-                    Shape[i] = cur_shape;
-                    Stride[i] = cur_stride;
+                    Shape[targetIdx] = cur_shape;
+                    Stride[targetIdx] = cur_stride;
                 }
             }
             NDim = targetNDim;
diff --git a/csharp/Tensor.NET/Tensor/Common/TensorMemory.cs b/csharp/Tensor.NET/Tensor/Common/TensorMemory.cs
new file mode 100644
index 0000000..d2c27b6
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/TensorMemory.cs
@@ -0,0 +1,37 @@
+using System.Runtime.InteropServices;
+using System.Buffers;
+using System.Linq;
+using Tensornet.Native;
+
+namespace Tensornet.Common{
+    internal interface ITensorMemory{
+        void Pin(out MemoryHandle handle);
+    }
+    internal class TensorMemory<T> : ITensorMemory where T : struct{
+        private Memory<T> _memory;
+        public TensorMemory(int length){
+            _memory = new T[length];
+        }
+        public TensorMemory(T[] data, bool reuse = false){
+            if(reuse){
+                _memory = data;
+            }
+            else{
+                _memory = new T[data.Length];
+                data.CopyTo(AsSpan());
+            }
+        }
+        public TensorMemory(Span<T> data){
+            data.CopyTo(AsSpan());
+        }
+        public Span<T> AsSpan(){
+            return _memory.Span;
+        }
+        public Span<Byte> AsByteSpan(){
+            return MemoryMarshal.Cast<T, byte>(_memory.Span);
+        }
+        public void Pin(out MemoryHandle handle){
+            handle = _memory.Pin();
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Num.NET/Tensor/Common/TensorType.cs b/csharp/Tensor.NET/Tensor/Common/TensorType.cs
similarity index 80%
rename from csharp/Num.NET/Tensor/Common/TensorType.cs
rename to csharp/Tensor.NET/Tensor/Common/TensorType.cs
index 897219f..0ee5bb9 100644
--- a/csharp/Num.NET/Tensor/Common/TensorType.cs
+++ b/csharp/Tensor.NET/Tensor/Common/TensorType.cs
@@ -1,7 +1,7 @@
-using Numnet.Native;
-using Numnet.Exceptions;
+using Tensornet.Native;
+using Tensornet.Exceptions;
 
-namespace Numnet.Common{
+namespace Tensornet.Common{
     internal sealed class TensorTypeInfo
     {
         private static readonly Dictionary<Type, TensorTypeInfo> _typeInfoMap;
@@ -9,21 +9,26 @@ internal sealed class TensorTypeInfo
         private static readonly Dictionary<DType, int> _sizeMap;
         public readonly DType _dtype;
         public readonly int _size;
+        /// <summary>
+        /// The priority in type conversion. The larger the value is, the higher priority it has.
+        /// </summary>
+        public readonly int _priority;
         static TensorTypeInfo(){
             _typeInfoMap = new Dictionary<Type, TensorTypeInfo>()
             {
-                { typeof(float), new TensorTypeInfo( DType.Float32, sizeof(float)) },
-                { typeof(int), new TensorTypeInfo( DType.Int32, sizeof(int)) },
-                { typeof(long), new TensorTypeInfo( DType.Int64, sizeof(long)) },
-                { typeof(bool), new TensorTypeInfo( DType.Bool, sizeof(bool)) },
-                { typeof(double), new TensorTypeInfo( DType.Float64, sizeof(double)) }
+                { typeof(float), new TensorTypeInfo( DType.Float32, sizeof(float), 4) },
+                { typeof(int), new TensorTypeInfo( DType.Int32, sizeof(int), 2) },
+                { typeof(long), new TensorTypeInfo( DType.Int64, sizeof(long), 3) },
+                { typeof(bool), new TensorTypeInfo( DType.Bool, sizeof(bool), 1) },
+                { typeof(double), new TensorTypeInfo( DType.Float64, sizeof(double), 5) }
             };
             _dtypeMap = _typeInfoMap.ToDictionary(k => k.Value._dtype, v => v.Key);
             _sizeMap = _typeInfoMap.ToDictionary(k => k.Value._dtype, v => v.Value._size);
         }
-        public TensorTypeInfo(DType dtype, int size){
+        public TensorTypeInfo(DType dtype, int size, int priority){
             _dtype = dtype;
             _size = size;
+            _priority = priority;
         }
         public static TensorTypeInfo GetTypeInfo(Type type){
             TensorTypeInfo res;
diff --git a/csharp/Tensor.NET/Tensor/Common/TypeConvert.cs b/csharp/Tensor.NET/Tensor/Common/TypeConvert.cs
new file mode 100644
index 0000000..4375c00
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/TypeConvert.cs
@@ -0,0 +1,24 @@
+using Tensornet.Native;
+using Tensornet.Common;
+using Tensornet.Native.Param;
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Convert the tensor to the target type.
+        /// Please note that whether the target type is same of different, a new memory will be alloced. Therefore, it could alse be used as a copy method.
+        /// </summary>
+        /// <typeparam name="TD"></typeparam>
+        /// <returns></returns>
+        public Tensor<TD> ToTensor<TD>() where TD : struct, IEquatable<TD>, IConvertible{
+            Tensor<TD> res = new Tensor<TD>(new TensorLayout(TLayout, TensorTypeInfo.GetTypeInfo(typeof(TD))._dtype));
+            TypeConvertInternal(TMemory, res.TMemory, TLayout, res.TLayout);
+            return res;
+        }
+        private static unsafe void TypeConvertInternal(ITensorMemory mA, ITensorMemory mB, TensorLayout lA, TensorLayout lB){
+            TypeConvertParam param = new TypeConvertParam() { targetType = lB.DType };
+            IntPtr status = NativeExecutor.Execute(NativeApi.TypeConvert, mA, mB, lA, lB, new IntPtr(&param), Tensor<T>.Provider);
+            NativeStatus.AssertOK(status);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Common/Unsqueeze.cs b/csharp/Tensor.NET/Tensor/Common/Unsqueeze.cs
new file mode 100644
index 0000000..20ac2e2
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Common/Unsqueeze.cs
@@ -0,0 +1,14 @@
+
+namespace Tensornet{
+    public partial class Tensor<T>{
+        /// <summary>
+        /// Expand the shape of a tensor. Insert a new axis that will appear at the axis position in the expanded array shape.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.expand_dims.html?highlight=expand_dims#numpy.expand_dims
+        /// </summary>
+        /// <param name="axis"> Position in the expanded axes where the new axis (or axes) is placed. </param>
+        /// <returns></returns>
+        public Tensor<T> Unsqueeze(int axis){
+            return new Tensor<T>(this.TMemory, this.TLayout.AddAxis(axis, 1, 0));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Scalar.cs b/csharp/Tensor.NET/Tensor/Scalar.cs
new file mode 100644
index 0000000..879d7bd
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Scalar.cs
@@ -0,0 +1,371 @@
+using Tensornet.Native;
+using Tensornet.Common;
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    [Obsolete("The class may be removed in the future version.")]
+    public class Scalar : IEquatable<Scalar>{
+        public DType DataType{ get; internal set; }
+        public Scalar<T> AsScalar<T>() where T : struct{
+            return new Scalar<T>(GetValue<T>());
+        }
+        public virtual T GetValue<T>() where T : struct{
+            throw new NotImplementedException();
+        }
+        public virtual void SetValue<T>(T value){
+            throw new NotImplementedException();
+        }
+        public static implicit operator Scalar(int value){
+            return new Scalar<int>(value);
+        }
+        public static implicit operator Scalar(long value){
+            return new Scalar<long>(value);
+        }
+        public static implicit operator Scalar(float value){
+            return new Scalar<float>(value);
+        }
+        public static implicit operator Scalar(double value){
+            return new Scalar<double>(value);
+        }
+        public static implicit operator Scalar(bool value){
+            return new Scalar<bool>(value);
+        }
+        public override bool Equals(object? obj)
+        {
+            if (obj is null || GetType() != obj.GetType())
+            {
+                return false;
+            }
+            if (ReferenceEquals(this, obj)) return true;
+            return Equals((Scalar) obj);
+        }
+        public bool Equals(Scalar? other)
+        {
+            if(other is null || DataType != other.DataType){
+                return false;
+            }
+            return this == other;
+        }
+        
+        // override object.GetHashCode
+        public override int GetHashCode()
+        {
+            int valueHash = DataType switch
+            {
+                DType.Bool => GetValue<bool>().GetHashCode() % 0x3f3f3f3f,
+                DType.Int32 => GetValue<int>().GetHashCode() % 0x3f3f3f3f,
+                DType.Int64 => GetValue<long>().GetHashCode() % 0x3f3f3f3f,
+                DType.Float32 => GetValue<float>().GetHashCode() % 0x3f3f3f3f,
+                DType.Float64 => GetValue<double>().GetHashCode() % 0x3f3f3f3f,
+                _ => throw new NotImplementedException()
+            };
+            return (valueHash + (int)DataType * 0x0cfcfcfc) % 0x3f3f3f3f;
+        }
+        public static Scalar operator +(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return new Scalar<double>(lhs.GetValue<double>() + rhs.GetValue<double>());
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return new Scalar<float>(lhs.GetValue<float>() + rhs.GetValue<float>());
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() + rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() + rhs.GetValue<int>());
+            }
+            else if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                return new Scalar<bool>(lhs.GetValue<bool>() || rhs.GetValue<bool>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator -(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return new Scalar<double>(lhs.GetValue<double>() - rhs.GetValue<double>());
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return new Scalar<float>(lhs.GetValue<float>() - rhs.GetValue<float>());
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() - rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() - rhs.GetValue<int>());
+            }
+            else if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                throw new InvalidOperationException("Two bool scalar cannot execute sub operator.");
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator *(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                throw new InvalidOperationException("Bool scalar is not allowed for mul operation.");
+            }
+            else if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return new Scalar<double>(lhs.GetValue<double>() - rhs.GetValue<double>());
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return new Scalar<float>(lhs.GetValue<float>() - rhs.GetValue<float>());
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() - rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() - rhs.GetValue<int>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator /(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                throw new InvalidOperationException("Bool scalar is not allowed for div operation.");
+            }
+            else if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return new Scalar<double>(lhs.GetValue<double>() / rhs.GetValue<double>());
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return new Scalar<float>(lhs.GetValue<float>() / rhs.GetValue<float>());
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() / rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() / rhs.GetValue<int>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator %(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                throw new InvalidOperationException("Bool scalar is not allowed for mod operation.");
+            }
+            else if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return new Scalar<double>(lhs.GetValue<double>() % rhs.GetValue<double>());
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return new Scalar<float>(lhs.GetValue<float>() % rhs.GetValue<float>());
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() % rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() % rhs.GetValue<int>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator &(Scalar lhs, Scalar rhs){
+            if(lhs.DataType != rhs.DataType){
+                throw new MismatchedTypeException($"Only two scalar with same time could do & operation. " + 
+                    "But the one is {Enum.GetName<DType>(lhs.DataType)}, the other is {Enum.GetName<DType>(rhs.DataType)}");
+            }
+            if(lhs.DataType == DType.Float64){
+                throw new MismatchedTypeException($"Float64 is not allowed in & operation.");
+            }
+            else if(lhs.DataType == DType.Float32){
+                throw new MismatchedTypeException($"Float32 is not allowed in & operation.");
+            }
+            else if(lhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() & rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() & rhs.GetValue<int>());
+            }
+            else if(lhs.DataType == DType.Bool){
+                return new Scalar<bool>(lhs.GetValue<bool>() & rhs.GetValue<bool>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator |(Scalar lhs, Scalar rhs){
+            if(lhs.DataType != rhs.DataType){
+                throw new MismatchedTypeException($"Only two scalar with same time could do | operation. " + 
+                    "But the one is {Enum.GetName<DType>(lhs.DataType)}, the other is {Enum.GetName<DType>(rhs.DataType)}");
+            }
+            if(lhs.DataType == DType.Float64){
+                throw new MismatchedTypeException($"Float64 is not allowed in | operation.");
+            }
+            else if(lhs.DataType == DType.Float32){
+                throw new MismatchedTypeException($"Float32 is not allowed in | operation.");
+            }
+            else if(lhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() | rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() | rhs.GetValue<int>());
+            }
+            else if(lhs.DataType == DType.Bool){
+                return new Scalar<bool>(lhs.GetValue<bool>() | rhs.GetValue<bool>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static Scalar operator ^(Scalar lhs, Scalar rhs){
+            if(lhs.DataType != rhs.DataType){
+                throw new MismatchedTypeException($"Only two scalar with same time could do ^ operation. " + 
+                    "But the one is {Enum.GetName<DType>(lhs.DataType)}, the other is {Enum.GetName<DType>(rhs.DataType)}");
+            }
+            if(lhs.DataType == DType.Float64){
+                throw new MismatchedTypeException($"Float64 is not allowed in ^ operation.");
+            }
+            else if(lhs.DataType == DType.Float32){
+                throw new MismatchedTypeException($"Float32 is not allowed in ^ operation.");
+            }
+            else if(lhs.DataType == DType.Int64){
+                return new Scalar<long>(lhs.GetValue<long>() ^ rhs.GetValue<long>());
+            }
+            else if(lhs.DataType == DType.Int32){
+                return new Scalar<int>(lhs.GetValue<int>() ^ rhs.GetValue<int>());
+            }
+            else if(lhs.DataType == DType.Bool){
+                return new Scalar<bool>(lhs.GetValue<bool>() ^ rhs.GetValue<bool>());
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static bool operator ==(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return lhs.GetValue<double>() == rhs.GetValue<double>();
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return lhs.GetValue<float>() == rhs.GetValue<float>();
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return lhs.GetValue<long>() == rhs.GetValue<long>();
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return lhs.GetValue<int>() == rhs.GetValue<int>();
+            }
+            else if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                return lhs.GetValue<bool>() == rhs.GetValue<bool>();
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static bool operator !=(Scalar lhs, Scalar rhs){
+            return !(lhs == rhs);
+        }
+        public static bool operator >(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                throw new MismatchedTypeException("Bool scalar is not allowed to do comparison.");
+            }
+            else if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return lhs.GetValue<double>() > rhs.GetValue<double>();
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return lhs.GetValue<float>() > rhs.GetValue<float>();
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return lhs.GetValue<long>() > rhs.GetValue<long>();
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return lhs.GetValue<int>() > rhs.GetValue<int>();
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static bool operator <(Scalar lhs, Scalar rhs){
+            if(lhs.DataType == DType.Bool || rhs.DataType == DType.Bool){
+                throw new MismatchedTypeException("Bool scalar is not allowed to do comparison.");
+            }
+            else if(lhs.DataType == DType.Float64 || rhs.DataType == DType.Float64){
+                return lhs.GetValue<double>() < rhs.GetValue<double>();
+            }
+            else if(lhs.DataType == DType.Float32 || rhs.DataType == DType.Float32){
+                return lhs.GetValue<float>() < rhs.GetValue<float>();
+            }
+            else if(lhs.DataType == DType.Int64 || rhs.DataType == DType.Int64){
+                return lhs.GetValue<long>() < rhs.GetValue<long>();
+            }
+            else if(lhs.DataType == DType.Int32 || rhs.DataType == DType.Int32){
+                return lhs.GetValue<int>() < rhs.GetValue<int>();
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        public static bool operator >=(Scalar lhs, Scalar rhs){
+            return !(lhs < rhs);
+        }
+        public static bool operator <=(Scalar lhs, Scalar rhs){
+            return !(lhs > rhs);
+        }
+    }
+    public class Scalar<T> : Scalar where T : struct{
+        public Scalar(T value){
+            Value = value;
+            DataType = TensorTypeInfo.GetTypeInfo(typeof(T))._dtype;
+        }
+        public T Value{ get; set; }
+
+        /// <summary>
+        /// Get the value of the specified type. Since this function contains casting, 
+        /// if you already have an instance of Scalar<T>, please use the Value property directly.
+        /// </summary>
+        /// <typeparam name="V"></typeparam>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        public override V GetValue<V>() where V : struct{
+            if(typeof(V) == typeof(bool)){
+                return (V)(Convert.ToBoolean(Value) as ValueType);
+            }
+            else if(typeof(V) == typeof(double)){
+                return (V)(Convert.ToDouble(Value) as ValueType);
+            }
+            else if(typeof(V) == typeof(float)){
+                return (V)(Convert.ToSingle(Value) as ValueType);
+            }
+            else if(typeof(V) == typeof(long)){
+                return (V)(Convert.ToInt64(Value) as ValueType);
+            }
+            else if(typeof(V) == typeof(int)){
+                return (V)(Convert.ToInt32(Value) as ValueType);
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+        
+        /// <summary>
+        /// Get the value of the specified type. Since this function contains casting, 
+        /// if you already have an instance of Scalar<T>, please use the Value property directly.
+        /// </summary>
+        /// <typeparam name="V"></typeparam>
+        /// <param name="value"></param>
+        public override void SetValue<V>(V value){
+            if(value is null){
+                throw new InvalidArgumentException("Null value is not allowed to be set to scalar.");
+            }
+            if(typeof(T) == typeof(double)){
+                Value = (T)(Convert.ToDouble(value) as ValueType);
+            }
+            else if(typeof(T) == typeof(float)){
+                Value = (T)(Convert.ToSingle(value) as ValueType);
+            }
+            else if(typeof(T) == typeof(long)){
+                Value = (T)(Convert.ToInt64(value) as ValueType);
+            }
+            else if(typeof(T) == typeof(int)){
+                Value = (T)(Convert.ToInt32(value) as ValueType);
+            }
+            else if(typeof(T) == typeof(Boolean)){
+                Value = (T)(Convert.ToBoolean(value) as ValueType);
+            }
+            else{
+                throw new NotImplementedException();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Serializer/Tensor.Reader.cs b/csharp/Tensor.NET/Tensor/Serializer/Tensor.Reader.cs
new file mode 100644
index 0000000..ca59afd
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Serializer/Tensor.Reader.cs
@@ -0,0 +1,76 @@
+using System.IO;
+using System.Text;
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    public static class TensorReader{
+        private static byte[] _magicNumber;
+        private static byte[] _versionNumber;
+
+        static TensorReader(){
+            _magicNumber = Encoding.ASCII.GetBytes("Tensor.NET");
+            _versionNumber = Encoding.ASCII.GetBytes(Tensor.VersionNumber);
+        }
+
+        public static Tensor<T> Read<T>(string path) where T : struct, IEquatable<T>, IConvertible{
+            TensorLayout layout;
+            var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
+            CheckMagicAndVersion(fs);
+            layout = GetLayoutFromHeader(fs);
+            var res = layout.DType switch
+            {
+                DType.Int32 => new Tensor<int>(ReadDataInternal<int>(fs, layout), layout).ToTensor<T>(),
+                DType.Int64 => new Tensor<long>(ReadDataInternal<long>(fs, layout), layout).ToTensor<T>(),
+                DType.Float32 => new Tensor<float>(ReadDataInternal<float>(fs, layout), layout).ToTensor<T>(),
+                DType.Float64 => new Tensor<double>(ReadDataInternal<double>(fs, layout), layout).ToTensor<T>(),
+                DType.Bool => new Tensor<bool>(ReadDataInternal<bool>(fs, layout), layout).ToTensor<T>(),
+                _ => throw new NNSerializeException("Unsupported data type in serialization.")
+            };
+            fs.Close();
+            return res;
+        }
+
+        private static void CheckMagicAndVersion(FileStream fs){
+            for (int i = 0; i < _magicNumber.Length; i++){
+                if(fs.ReadByte() != _magicNumber[i]){
+                    throw new NNSerializeException("Wrong magic number.");
+                }
+            }
+            fs.Position += _versionNumber.Length;
+        }
+
+        private static TensorLayout GetLayoutFromHeader(FileStream fs){
+            var int32Buffer = new byte[4];
+            TensorLayout res = new TensorLayout();
+            fs.Read(int32Buffer, 0, 4);
+            res.DType = (DType)BitConverter.ToInt32(int32Buffer, 0);
+            fs.Read(int32Buffer, 0, 4);
+            res.NDim = BitConverter.ToInt32(int32Buffer, 0);
+            for (int i = 0; i < TensorLayout.MAX_NDIM; i++)
+            {
+                fs.Read(int32Buffer, 0, 4);
+                res.Shape[i] = BitConverter.ToInt32(int32Buffer, 0);
+            }
+            res.InitContiguousLayout();
+            return res;
+        }
+
+        private static void ReadContiguousData<T>(FileStream fs, TensorMemory<T> memory) where T : struct, IEquatable<T>, IConvertible{
+            fs.Read(memory.AsByteSpan());
+        }
+
+        private static TensorMemory<T> ReadDataInternal<T>(FileStream fs, TensorLayout layout) where T : struct, IEquatable<T>, IConvertible{
+            TensorMemory<T> res = new TensorMemory<T>(layout.TotalElemCount());
+            ReadContiguousData(fs, res);
+            return res;
+        }
+    }
+
+    public static partial class Tensor{
+        public static Tensor<T> Load<T>(string path) where T : struct, IEquatable<T>, IConvertible{
+            return TensorReader.Read<T>(path);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Serializer/Tensor.writer.cs b/csharp/Tensor.NET/Tensor/Serializer/Tensor.writer.cs
new file mode 100644
index 0000000..59bd11c
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Serializer/Tensor.writer.cs
@@ -0,0 +1,84 @@
+using System.IO;
+using System.Text;
+using Tensornet.Common;
+using Tensornet.Native;
+using Tensornet.Exceptions;
+
+namespace Tensornet{
+    public enum TensorSerializationMode
+    {
+        Auto = 0,
+        TensorNET,
+        Numpy
+    }
+    public static class TensorWriter{
+        private static byte[] _magicNumber;
+        private static byte[] _versionNumber;
+
+        static TensorWriter(){
+            _magicNumber = Encoding.ASCII.GetBytes("Tensor.NET");
+            _versionNumber = Encoding.ASCII.GetBytes(Tensor.VersionNumber);
+        }
+
+        public static void Write<T>(string path, Tensor<T> tensor, TensorSerializationMode mode = TensorSerializationMode.TensorNET) where T : struct, IEquatable<T>, IConvertible{
+            using(var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)){
+                WriteHeader(fs, tensor.TLayout);
+                if(tensor.TLayout.IsContiguous()){
+                    WriteContiguousData(fs, tensor.TMemory);
+                }
+                else{
+                    WriteStrideData(fs, tensor);
+                }
+            }
+        }
+
+        public static async void WriteAsync<T>(string path, Tensor<T> tensor) where T : struct, IEquatable<T>, IConvertible{
+            throw new NotImplementedException();
+        }
+
+        private static void WriteMagicAndVersion(FileStream fs){
+            fs.Write(_magicNumber);
+            fs.Write(_versionNumber);
+        }
+
+        private static void WriteHeader(FileStream fs, TensorLayout layout){
+            WriteMagicAndVersion(fs);
+            fs.Write(BitConverter.GetBytes(((int)layout.DType)));
+            fs.Write(BitConverter.GetBytes(layout.NDim));
+            for (int i = 0; i < TensorLayout.MAX_NDIM; i++)
+            {
+                fs.Write(BitConverter.GetBytes(layout.Shape[i]));
+            }
+        }
+
+        private static void WriteContiguousData<T>(FileStream fs, TensorMemory<T> memory) where T : struct, IEquatable<T>, IConvertible{
+            fs.Write(memory.AsByteSpan());
+        }
+
+        private static void WriteStrideData<T>(FileStream fs, Tensor<T> tensor) where T : struct, IEquatable<T>, IConvertible{
+            byte[] data = new byte[TensorTypeInfo.GetTypeSize(tensor.TLayout.DType)];
+            Span<byte> dataSpan = new Span<byte>(data);
+            foreach(T item in tensor){
+                bool actionRes = tensor.TLayout.DType switch
+                {
+                    DType.Int32 => BitConverter.TryWriteBytes(dataSpan, Convert.ToInt32(item)),
+                    DType.Int64 => BitConverter.TryWriteBytes(dataSpan, Convert.ToInt64(item)),
+                    DType.Float32 => BitConverter.TryWriteBytes(dataSpan, Convert.ToSingle(item)),
+                    DType.Float64 => BitConverter.TryWriteBytes(dataSpan, Convert.ToDouble(item)),
+                    DType.Bool => BitConverter.TryWriteBytes(dataSpan, Convert.ToBoolean(item)),
+                    _ => false
+                };
+                if(!actionRes){
+                    throw new NNRuntimeException("Failed to write byte to target span.");
+                }
+                fs.Write(dataSpan);
+            }
+        }
+    }
+
+    public partial class Tensor<T>{
+        public void Save(string path, TensorSerializationMode mode = TensorSerializationMode.TensorNET){
+            TensorWriter.Write(path, this, mode);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Tensor.BoolIndex.cs b/csharp/Tensor.NET/Tensor/Tensor.BoolIndex.cs
new file mode 100644
index 0000000..e9bfd2d
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Tensor.BoolIndex.cs
@@ -0,0 +1,34 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T> where T : struct, IEquatable<T>, IConvertible{
+        public Tensor<T> this[Tensor<bool> index]{
+            get{
+                Tensor<T> res = new Tensor<T>(new TensorLayout(TLayout, true));
+                BoolIndexInternal(this, res, index);
+                return res;
+            }
+            set{
+                if(value.TLayout.CanBroadCastTo(this.TLayout)){
+                    var refer = new Tensor<T>(value.TMemory, value.TLayout.Broadcast(this.TLayout));
+                    InplaceOperation.Execute<T>(this, index, refer);
+                }
+                else{
+                    throw new MismatchedShapeException($"Cannot broadcast from {value.TLayout as TensorShape} to {TLayout as TensorShape}.");
+                }
+            }
+        }
+        private static unsafe void BoolIndexInternal(Tensor<T> src, Tensor<T> dst, Tensor<bool> index){
+            if(index.TLayout.CanBroadCastTo(dst.TLayout)){
+                var indexLayout = index.TLayout.Broadcast(dst.TLayout);
+                IntPtr status = NativeExecutor.Execute(NativeApi.BoolIndex, src.TMemory, index.TMemory, dst.TMemory, src.TLayout, indexLayout, dst.TLayout, IntPtr.Zero, Tensor<T>.Provider);
+                NativeStatus.AssertOK(status);
+            }
+            else{
+                throw new MismatchedShapeException($"Cannot broadcast from {(index.TLayout as TensorShape)} to {(dst.TLayout as TensorShape)}.");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Tensor.Enumerator.cs b/csharp/Tensor.NET/Tensor/Tensor.Enumerator.cs
new file mode 100644
index 0000000..562c2c9
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Tensor.Enumerator.cs
@@ -0,0 +1,76 @@
+using System.Collections;
+
+namespace Tensornet{
+    public class TensorEnumerator<T> : IEnumerator, IEnumerator<T> where T : struct, IEquatable<T>, IConvertible{
+        private readonly Tensor<T> _tensor;
+        private int _index;
+        private T _current;
+        private int[] indices;
+
+        internal TensorEnumerator(Tensor<T> tensor)
+        {
+            _tensor = tensor;
+            _index = 0;
+            _current = default;
+            indices = new int[4] { 0, 0, 0, 0 };
+        }
+
+        public void Dispose()
+        {
+        }
+
+        public bool MoveNext()
+        {
+            Tensor<T> localTensor = _tensor;
+
+            if ((uint)_index < (uint)localTensor.TLayout.TotalElemCount())
+            {
+                _current = localTensor.AsSpan()[_index];
+                // _index++;
+                IndexIncrease();
+                return true;
+            }
+            return MoveNextRare();
+        }
+
+        private bool MoveNextRare()
+        {
+            _index = _tensor.TLayout.TotalElemCount() + 1;
+            _current = default;
+            return false;
+        }
+
+        private void IndexIncrease(){
+            indices[_tensor.TLayout.NDim - 1]++;
+            _index += _tensor.TLayout.Stride[_tensor.TLayout.NDim - 1];
+            for (int i = _tensor.TLayout.NDim - 1; i >= 1; i--){
+                if(indices[i] < _tensor.TLayout.Shape[i]) break;
+                else{
+                    indices[i - 1]++;
+                    _index = _index + _tensor.TLayout.Stride[i - 1] - _tensor.TLayout.Stride[i] * indices[i];
+                    indices[i] = 0;
+                }
+            }
+        }
+
+        public T Current => _current;
+
+        object IEnumerator.Current
+        {
+            get
+            {
+                if (_index == 0 || _index == _tensor.TLayout.TotalElemCount() + 1)
+                {
+                    throw new InvalidOperationException();
+                }
+                return Current;
+            }
+        }
+
+        void IEnumerator.Reset()
+        {
+            _index = 0;
+            _current = default;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Tensor.Version.cs b/csharp/Tensor.NET/Tensor/Tensor.Version.cs
new file mode 100644
index 0000000..edb1602
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Tensor.Version.cs
@@ -0,0 +1,5 @@
+namespace Tensornet{
+    public static partial class Tensor{
+        public static string VersionNumber { get;} = "1.0.0";
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Tensor.Where.cs b/csharp/Tensor.NET/Tensor/Tensor.Where.cs
new file mode 100644
index 0000000..7e5959c
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Tensor.Where.cs
@@ -0,0 +1,41 @@
+using Tensornet.Native;
+using Tensornet.Exceptions;
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor<T> where T : struct, IEquatable<T>, IConvertible{
+        /// <summary>
+        /// Return elements chosen from trueCase or falseCase depending on condition.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.where.html?highlight=where
+        /// </summary>
+        /// <param name="condition"> The condition to decide whether use trueCase or falseCase</param>
+        /// <param name="trueCase"> The value to fill when the condition is true. </param>
+        /// <param name="falseCase"> The value to fill when the condition is false. </param>
+        /// <returns></returns>
+        public Tensor<T> Where(Func<T, bool> condition, T trueCase, T falseCase){
+            return OnElemOperation.Execute<T, T>(this, x =>
+            {
+                if (condition(x)) return trueCase;
+                else return falseCase;
+            });
+        }
+    }
+
+    public static partial class Tensor{
+        /// <summary>
+        /// Return elements chosen from trueCase or falseCase depending on a bool tensor.
+        /// For details, please refer to https://numpy.org/doc/stable/reference/generated/numpy.where.html?highlight=where
+        /// </summary>
+        /// <param name="conditionTensor"> The condition to decide whether use trueCase or falseCase</param>
+        /// <param name="trueCase"> The value to fill when the condition is true. </param>
+        /// <param name="falseCase"> The value to fill when the condition is false. </param>
+        /// <returns></returns>
+        public static Tensor<T> Where<T>(Tensor<bool> conditionTensor, T trueCase, T falseCase) where T : struct, IEquatable<T>, IConvertible{
+            return OnElemOperation.Execute<bool, T>(conditionTensor, x =>
+            {
+                if (x) return trueCase;
+                else return falseCase;
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Tensor.cs b/csharp/Tensor.NET/Tensor/Tensor.cs
new file mode 100644
index 0000000..1551578
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Tensor.cs
@@ -0,0 +1,180 @@
+using Tensornet.Native;
+using System.Text;
+using Tensornet.Common;
+using Tensornet.Exceptions;
+using System.Collections;
+
+namespace Tensornet{
+    public partial class Tensor<T> : IEnumerable, IEnumerable<T>, ICloneable where T : struct, IEquatable<T>, IConvertible
+    {
+        internal TensorLayout TLayout{get; set; }
+        internal TensorMemory<T> TMemory{ get; set; }
+        public Span<int> Shape{
+            get{
+                return TLayout.Shape.AsSpan(0, TLayout.NDim);
+            }
+        }
+        public int Dim{get { return TLayout.NDim; } }
+        public DType DataType{get { return TLayout.DType; } }
+        public static NativeProvider Provider { get; set; } = NativeProvider.Naive;
+        internal Tensor(TensorMemory<T> memory, TensorLayout layout){
+            TMemory = memory;
+            TLayout = layout;
+        }
+        internal Tensor(TensorLayout layout){
+            TLayout = layout;
+            TMemory = new TensorMemory<T>(layout.TotalElemCount());
+        }
+        internal Tensor(TensorShape shape, DType dtype){
+            TLayout = new TensorLayout(shape, dtype);
+            TMemory = new TensorMemory<T>(shape.TotalElemCount());
+        }
+        /// <summary>
+        /// Only used to generate a scalar.
+        /// </summary>
+        /// <param name="value"></param>
+        internal Tensor(T value){
+            TMemory = new TensorMemory<T>(new T[] { value });
+            TLayout = new TensorLayout(new int[] { 1 }, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+        }
+        internal int IndicesToPosition(params int[] indices){
+            if(indices.Length != TLayout.NDim){
+                throw new InvalidArgumentException($"Index does not have same dims with tensor, " + 
+                    $"the index is {indices.Length} dims but the tensor is {TLayout.NDim}.");
+            }
+            int res = 0;
+            for (int i = 0; i < TLayout.NDim; i++) {
+                if(indices[i] < -1 || indices[i] >= TLayout.Shape[i]){
+                    throw new InvalidArgumentException($"{i}th index exceeds the bound of the shape. Index is {indices[i]}, limit is {TLayout.Shape[i]}.");
+                }
+                res += indices[i] * TLayout.Stride[i];
+            }
+            return res + TLayout.Offset;
+        }
+        public Span<T> AsSpan(){
+            return TMemory.AsSpan();
+        }
+
+        public T this[params int[] indices]{
+            get{
+                return AsSpan()[IndicesToPosition(indices)];
+            }
+            set{
+                AsSpan()[IndicesToPosition(indices)] = value;
+            }
+        }
+
+        /// <summary>
+        /// Make the tensor contiguous. Note that if the tensor is already contiguous, the method will return itself.
+        /// If the tensor is not contiguous, a new contiguous tensor will be returned.
+        /// </summary>
+        /// <returns></returns>
+        public Tensor<T> ToContiguousTensor(){
+            Tensor<T> res = new Tensor<T>(new TensorLayout(TLayout as TensorShape, TLayout.DType));
+            this.CopyTo(res);
+            return res;
+        }
+
+        /// <summary>
+        /// Copy the data from one tensor to another tensor.
+        /// Please note that it will only check the total element count instead of the whole shape.
+        /// </summary>
+        /// <param name="target"> A contiguous target tensor</param>
+        /// <exception cref="InvalidArgumentException"></exception>
+        /// <exception cref="MismatchedShapeException"></exception>
+        public void CopyTo(Tensor<T> target){
+            if(!target.TLayout.IsContiguous()){
+                throw new InvalidArgumentException("Cannot copy the data to a tensor which is not contiguous.");
+            }
+            if(target.TLayout.TotalElemCount() != TLayout.TotalElemCount()){
+                throw new MismatchedShapeException("Cannot copy a tensor to another tensor whose total element count is different.");
+            }
+            var spanTarget = target.AsSpan();
+            int i = 0;
+            foreach(var data in this){
+                spanTarget[i++] = data;
+            }
+        }
+
+        // Returns an enumerator for this list with the given
+        // permission for removal of elements. If modifications made to the list
+        // while an enumeration is in progress, the MoveNext and
+        // GetObject methods of the enumerator will throw an exception.
+        //
+        public TensorEnumerator<T> GetEnumerator()
+            => new TensorEnumerator<T>(this);
+ 
+        IEnumerator<T> IEnumerable<T>.GetEnumerator()
+            => new TensorEnumerator<T>(this);
+ 
+        IEnumerator IEnumerable.GetEnumerator()
+            => new TensorEnumerator<T>(this);
+
+        public object Clone(){
+            if(TLayout.IsContiguous()){
+                return new Tensor<T>(new TensorMemory<T>(TMemory.AsSpan()), new TensorLayout(TLayout));
+            }
+            else{
+                return ToContiguousTensor();
+            }
+        }
+
+        public override string ToString()
+        {
+            // Somtimes the tensor is not contiguous, so we need to convert the index calculated 
+            // by shape to the real index calculated by sride.
+            Func<int, int> getRealPos = idx => {
+                int res = 0;
+                int mod = 1;
+                for (int i = TLayout.NDim - 1; i >= 1; i--) mod *= TLayout.Shape[i];
+                for (int i = 0; i < TLayout.NDim; i++) {
+                    int shape = idx / mod;
+                    idx -= shape * mod;
+                    res += shape * TLayout.Stride[i];
+                    if(i < TLayout.NDim - 1 ) mod /= TLayout.Shape[i + 1];
+                }
+                return res + TLayout.Offset;
+            };
+
+            StringBuilder r = new StringBuilder($"Tensor({TLayout.GetInfoString()}):\n");
+            for (int i = 0; i < TLayout.TotalElemCount(); i++) {
+                int mod = 1;
+                for (int j = TLayout.NDim - 1; j >= 0; j--) {
+                    mod *= TLayout.Shape[j];
+                    if (i % mod == 0) {
+                        r.Append("[");
+                    } else {
+                        break;
+                    }
+                }
+                r.Append(" ").Append(AsSpan()[getRealPos(i)]);
+
+                if ((i + 1) % TLayout.Shape[TLayout.NDim - 1] != 0) r.Append(",");
+
+                r.Append(" ");
+                mod = 1;
+                int hit_times = 0;
+                for (int j = TLayout.NDim - 1; j >= 0; j--) {
+                    mod *= TLayout.Shape[j];
+                    if ((i + 1) % mod == 0) {
+                        r.Append("]");
+                        hit_times++;
+                    } else {
+                        break;
+                    }
+                }
+                if (hit_times > 0 && hit_times < TLayout.NDim) {
+                    r.Append(",\n");
+                    for (int j = 0; j < TLayout.NDim - hit_times; j++) {
+                        r.Append(" ");
+                    }
+                }
+            }
+            // r.Append("\n");
+            return r.ToString();
+        }
+
+        public static explicit operator Tensor<T>(T value) => new Tensor<T>(value);
+        public static implicit operator Tensor<T>(Scalar<T> value) => new Tensor<T>(value.Value);
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Utilities/TensorGeneration.cs b/csharp/Tensor.NET/Tensor/Utilities/TensorGeneration.cs
new file mode 100644
index 0000000..72784cd
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Utilities/TensorGeneration.cs
@@ -0,0 +1,70 @@
+using System.Buffers;
+using System.Runtime.InteropServices;
+using Tensornet.Native;
+using Tensornet.Common;
+
+namespace Tensornet{
+    public partial class Tensor
+    {
+        /// <summary>
+        /// Generate a tensor from a span.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="data"> The span which contains the data. </param>
+        /// <param name="shape"> The target shape. </param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static Tensor<T> FromSpan<T>(Span<T> data, TensorShape shape) where T : struct, IEquatable<T>, IConvertible{
+            if(shape.TotalElemCount() != data.Length){
+                // TODO
+                throw new Exception();
+            }
+            TensorMemory<T> memory = new TensorMemory<T>(data);
+            return new Tensor<T>(memory, new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
+        }
+        /// <summary>
+        /// Generate a tensor from an array.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="data"> The array which contains the data. </param>
+        /// <param name="shape"> The target shape. </param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static Tensor<T> FromArray<T>(T[] data, TensorShape shape) where T : struct, IEquatable<T>, IConvertible{
+            if(shape.TotalElemCount() != data.Length){
+                // TODO
+                throw new Exception();
+            }
+            TensorMemory<T> memory = new TensorMemory<T>(data);
+            return new Tensor<T>(memory, new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype));
+        }
+        /// <summary>
+        /// Generate a tensor from an array.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="data"> The array which contains the data. </param>
+        /// <param name="shape"> The target shape. </param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static Tensor<T> FromArray<T>(T[] data, int[] shape) where T : struct, IEquatable<T>, IConvertible{
+            TensorLayout layout = new TensorLayout(shape, TensorTypeInfo.GetTypeInfo(typeof(T))._dtype);
+            if(layout.TotalElemCount() != data.Length){
+                // TODO
+                throw new Exception();
+            }
+            TensorMemory<T> memory = new TensorMemory<T>(data);
+            return new Tensor<T>(memory, layout);
+        }
+        /// <summary>
+        /// Generate a tensor from an IEnumerable.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="data"> The IEnumerable which contains the data. </param>
+        /// <param name="shape"> The target shape. </param>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public static Tensor<T> FromEnumerable<T>(IEnumerable<T> data, int[] shape) where T : struct, IEquatable<T>, IConvertible{
+            return FromArray(data.ToArray(), shape);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/Tensor.NET/Tensor/Utilities/TensorUtils.cs b/csharp/Tensor.NET/Tensor/Utilities/TensorUtils.cs
new file mode 100644
index 0000000..291636d
--- /dev/null
+++ b/csharp/Tensor.NET/Tensor/Utilities/TensorUtils.cs
@@ -0,0 +1,34 @@
+using Tensornet.Native;
+
+namespace Tensornet{
+    public static class TensorUtils
+    {
+        /// <summary>
+        /// Return if each value of the tensors are equal.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="a"></param>
+        /// <param name="b"></param>
+        /// <param name="err"></param>
+        /// <returns></returns>
+        [Obsolete("The method may be removed in the future version")]
+        public static bool IsValueEqual<T>(Tensor<T> a, Tensor<T> b, double err = 1e-5) where T : struct, IEquatable<T>, IConvertible{
+            if(Object.ReferenceEquals(a, b)) return true;
+            if(!a.TLayout.IsSameShape(b.TLayout)) return false;
+            int[] indices = new int[a.TLayout.NDim];
+            for (int i = 0; i < a.TLayout.TotalElemCount(); i++){
+                if(System.Math.Abs(Convert.ToDouble(a[indices]) - Convert.ToDouble(b[indices])) > err){
+                    return false;
+                }
+                indices[a.TLayout.NDim - 1]++;
+                for (int j = a.TLayout.NDim - 1; j >= 1; j--){
+                    if(indices[j] == a.TLayout.Shape[j]){
+                        indices[j - 1]++;
+                        indices[j] = 0;
+                    }
+                }
+            }
+            return true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorBasicTest/Broadcast.cs b/csharp/TensorBasicTest/Broadcast.cs
new file mode 100644
index 0000000..656eb6f
--- /dev/null
+++ b/csharp/TensorBasicTest/Broadcast.cs
@@ -0,0 +1,30 @@
+using NUnit.Framework;
+using Tensornet;
+using System;
+using System.Collections.Generic;
+
+namespace TensorBasicTest;
+
+public class BroadcastTest
+{
+    private List<Tuple<Tensor<int>, Tensor<int>>> _map;
+    [SetUp]
+    public void Setup()
+    {
+        _map = new List<Tuple<Tensor<int>, Tensor<int>>>(new Tuple<Tensor<int>, Tensor<int>>[]{
+            new Tuple<Tensor<int>, Tensor<int>>(Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[]{2, 3}), 
+                        Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, new int[]{4, 2, 3})),
+            new Tuple<Tensor<int>, Tensor<int>>(
+                Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[]{2, 1, 3}),
+                Tensor.FromArray<int>(new int[]{1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6}, new int[]{2, 4, 3}))
+        });
+    }
+
+    [Test]
+    public void Test1()
+    {
+        foreach(var (inp, truth) in _map){
+            Assert.IsTrue(TensorUtils.IsValueEqual(inp.Broadcast(truth.Shape), truth));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorBasicTest/Reshape.cs b/csharp/TensorBasicTest/Reshape.cs
new file mode 100644
index 0000000..2d07567
--- /dev/null
+++ b/csharp/TensorBasicTest/Reshape.cs
@@ -0,0 +1,30 @@
+using NUnit.Framework;
+using Tensornet;
+using System;
+using System.Collections.Generic;
+
+namespace TensorBasicTest;
+
+public class ReshapeTest
+{
+    private List<Tuple<Tensor<int>, Tensor<int>>> _map;
+    [SetUp]
+    public void Setup()
+    {
+        _map = new List<Tuple<Tensor<int>, Tensor<int>>>(new Tuple<Tensor<int>, Tensor<int>>[]{
+            new Tuple<Tensor<int>, Tensor<int>>(Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[]{2, 3}), 
+                        Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[]{3, 2})),
+            new Tuple<Tensor<int>, Tensor<int>>(
+                Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, new int[]{3, 8}), 
+                Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, new int[]{2, 2, 6}))
+        });
+    }
+
+    [Test]
+    public void Test1()
+    {
+        foreach(var (inp, truth) in _map){
+            Assert.IsTrue(TensorUtils.IsValueEqual(inp.Reshape(truth.Shape), truth));
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorBasicTest/Scalar.cs b/csharp/TensorBasicTest/Scalar.cs
new file mode 100644
index 0000000..3880d93
--- /dev/null
+++ b/csharp/TensorBasicTest/Scalar.cs
@@ -0,0 +1,39 @@
+using NUnit.Framework;
+using Tensornet;
+using System;
+using System.Collections.Generic;
+
+namespace TensorBasicTest;
+
+public class ScalarTest
+{
+    [SetUp]
+    public void Setup()
+    {
+
+    }
+
+    [Test]
+    public void TestConvert()
+    {
+        Scalar a = new Scalar<int>(123);
+        Scalar b = a.AsScalar<double>();
+        b += 0.001;
+        Assert.AreEqual(b.GetValue<float>(), 123.001f);
+        b.SetValue<int>(456);
+        Assert.AreEqual(b.GetValue<long>(), 456L);
+    }
+
+    [Test]
+    public void TestEqual()
+    {
+        Scalar a = new Scalar<int>(123);
+        Scalar b = a.AsScalar<double>();
+        b += 0.9;
+        Assert.IsTrue(b == 123.9);
+        Assert.IsFalse(a >= 124);
+        a.SetValue<double>(123.9);
+        Assert.IsFalse(a.Equals(b));
+        Assert.AreEqual(a.GetValue<double>(), 124.0);
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorBasicTest/Slice.cs b/csharp/TensorBasicTest/Slice.cs
new file mode 100644
index 0000000..e4d4c16
--- /dev/null
+++ b/csharp/TensorBasicTest/Slice.cs
@@ -0,0 +1,49 @@
+using NUnit.Framework;
+using Tensornet;
+using System;
+using System.Collections.Generic;
+
+namespace TensorBasicTest;
+
+public class SliceTest
+{
+    private List<Tuple<Tensor<int>, Tensor<int>>> _map;
+    [SetUp]
+    public void Setup()
+    {
+        _map = new List<Tuple<Tensor<int>, Tensor<int>>>(new Tuple<Tensor<int>, Tensor<int>>[]{
+            new Tuple<Tensor<int>, Tensor<int>>(Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[]{2, 3}), 
+                        Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[]{3, 2})),
+            new Tuple<Tensor<int>, Tensor<int>>(
+                Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, new int[]{3, 8}), 
+                Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6}, new int[]{2, 2, 6}))
+        });
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var src = Tensor.FromArray(new int[] { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6 }, new int[] { 3, 8 });
+        var dst = src[new Slice(1, 3), new Slice(3, 6)];
+        Tensor<int> truth = Tensor.FromArray<int>(new int[] { 6, 1, 2, 2, 3, 4 }, new int[] { 2, 3 });
+        Assert.IsTrue(TensorUtils.IsValueEqual(dst, truth));
+    }
+
+    [Test]
+    public void Test2()
+    {
+        var src = Tensor.FromArray(new int[] { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6 }, new int[] { 3, 8 });
+        var dst = src[new Slice(0, 3, 2), new Slice(1, 8, 3)];
+        Tensor<int> truth = Tensor.FromArray<int>(new int[] { 2, 5, 2, 6, 3, 6 }, new int[] { 2, 3 });
+        Assert.IsTrue(TensorUtils.IsValueEqual(dst, truth));
+    }
+
+    [Test]
+    public void Test3()
+    {
+        var src = Tensor.FromArray(new int[] { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6 }, new int[] { 3, 8 });
+        var dst = src[new Slice(1, 3), new Slice(5)];
+        Tensor<int> truth = Tensor.FromArray<int>(new int[] { 2, 4 }, new int[] { 2 });
+        Assert.IsTrue(TensorUtils.IsValueEqual(dst, truth));
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorBasicTest/TensorBasicTest.csproj b/csharp/TensorBasicTest/TensorBasicTest.csproj
index 3eb8282..59e3607 100644
--- a/csharp/TensorBasicTest/TensorBasicTest.csproj
+++ b/csharp/TensorBasicTest/TensorBasicTest.csproj
@@ -14,8 +14,8 @@
     <PackageReference Include="coverlet.collector" Version="3.1.0" />
   </ItemGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\Num.NET\Num.NET.csproj" />
+  <ItemGroup>
+    <ProjectReference Include="..\Tensor.NET\Tensor.NET.csproj" />
   </ItemGroup>
 
 </Project>
diff --git a/csharp/TensorBasicTest/UnitTest1.cs b/csharp/TensorBasicTest/UnitTest1.cs
deleted file mode 100644
index e6667e2..0000000
--- a/csharp/TensorBasicTest/UnitTest1.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using NUnit.Framework;
-
-namespace TensorBasicTest;
-
-public class Tests
-{
-    [SetUp]
-    public void Setup()
-    {
-        
-    }
-
-    [Test]
-    public void Test1()
-    {
-        Assert.Pass();
-    }
-}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/Eye.cs b/csharp/TensorOpTest/Eye.cs
new file mode 100644
index 0000000..d9a4e69
--- /dev/null
+++ b/csharp/TensorOpTest/Eye.cs
@@ -0,0 +1,35 @@
+using NUnit.Framework;
+using Tensornet;
+using Tensornet.Common;
+
+namespace TensorOpTest;
+
+public class EyeTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    // [Test]
+    // public void Test1()
+    // {
+    //     var t = Tensor.FromArray<int>(
+    //         new int[] { 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, new TensorShape(4, 5));
+    //     System.Diagnostics.Debugger.Launch();
+    //     var p = Tensor.Eye<int>(4, 5, 2);
+    //     Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    // }
+
+    // [Test]
+    // public void Test2()
+    // {
+    //     var t = Tensor.FromArray<double>(
+    //         new double[] { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
+    //                        0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 
+    //                        0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 
+    //                        0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, new TensorShape(8, 6));
+    //     var p = Tensor.Eye<double>(8, 6, -1);
+    //     Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    // }
+}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/Fill.cs b/csharp/TensorOpTest/Fill.cs
new file mode 100644
index 0000000..6757ed1
--- /dev/null
+++ b/csharp/TensorOpTest/Fill.cs
@@ -0,0 +1,25 @@
+using NUnit.Framework;
+using Tensornet;
+using Tensornet.Common;
+
+namespace TensorOpTest;
+
+public class FillTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var t = Tensor.FromArray<int>(
+            new int[] { 0, 0, 1, 0, 0, 0 }, new TensorShape(3, 2));
+        var truth = Tensor.FromArray<int>(
+            new int[] { 123, 123, 123, 123, 123, 123 }, new TensorShape(3, 2));
+        System.Diagnostics.Debugger.Launch();
+        t.Fill(123);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, truth));
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/Matmul.cs b/csharp/TensorOpTest/Matmul.cs
new file mode 100644
index 0000000..554b3c7
--- /dev/null
+++ b/csharp/TensorOpTest/Matmul.cs
@@ -0,0 +1,159 @@
+using NUnit.Framework;
+using Tensornet;
+
+namespace TensorOpTest;
+
+public class MatmulTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var a = Tensor.FromArray<int>(new int[] { 1, 2, 3, 4, 5, 6 }, new TensorShape(2, 3));
+        var b = Tensor.FromArray<int>(new int[] { -1, 1, 2, 1, -1, 3 }, new TensorShape(3, 2));
+        var t = Tensor.FromArray<int>(new int[] { 0, 12, 0, 27 }, new TensorShape(2, 2));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test2()
+    {
+        var a = Tensor.FromArray<int>(new int[] { 1, 3, 5, 7, 9 }, new TensorShape(5, 1));
+        var b = Tensor.FromArray<int>(new int[] { 2, 4, 6, 8, 10 }, new TensorShape(1, 5));
+        var t = Tensor.FromArray<int>(new int[] { 2,  4,  6,  8,  10, 6,  12, 18, 24, 30, 10, 20, 30,
+                    40, 50, 14, 28, 42, 56, 70, 18, 36, 54, 72, 90 }, new TensorShape(5, 5));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test3()
+    {
+        var a = Tensor.FromArray<double>(
+            new double[] { -8.437620268739593,  7.740016490265845,   -2.718748159717318,
+                            4.076362360501086,   -11.623399100527902, 15.142754042231019,
+                            -4.661652444132063,  19.803787411892657,  18.034918544512493,
+                            0.5484673220992633,  8.275595477083684,   -7.839590431114022,
+                            -6.525671288400314,  -6.6976349864874365, 11.678041249945768,
+                            8.027529844244178,   9.633496104375794,   19.21835118702979,
+                            1.855516944766265,   -4.416146677459146,  -1.6957639422062556,
+                            4.05269944529557,    -11.073620591053466, 5.481867755745512,
+                            -19.743764681683885, 10.823517335964155,  -13.170212197048649,
+                            -0.6085745188416105, -10.643371450680243, -2.9214692897250387,
+                            10.998063572711956,  -8.2473360940403,    -18.365489751487452,
+                            1.0113219544261298,  -14.207695003202518, -10.89741461633276,
+                            17.640823400385123,  -6.803936612665144,  -10.432236790357056,
+                            10.151183932088653,  11.632614090472163,  9.899002709432217,
+                            -16.0328862764357,   4.660828958932161,   11.278375899418531,
+                            -18.208043490908288, -8.304635778239406,  -1.5167989188120892,
+                            -10.563363578587627, -12.94383462875263,  -10.60842129295255,
+                            6.146827389030513,   -3.1127053415670645, -0.3788329301429343,
+                            5.065047819715378,   -19.485494518339422, -7.790652261044851,
+                            -15.76467722046241,  6.909490596651025,   4.4873495672112185 }, new TensorShape(1, 3, 4, 5));
+        var b = Tensor.FromArray<double>(
+            new double[] { -7.5827876234067375, -6.13534310952798,   -24.61466531083513,
+                            -29.340869427040992, -20.037274582710047, -4.86314945103641,
+                            -11.253241065283067, -21.142395003734165, -15.938173042067664,
+                            0.9085878891952781,  0.280189916113617,   -20.26590286965132,
+                            -28.689092469974103, 17.273583021966253,  -10.656336237819993,
+                            18.449527456757245,  20.628373813727876,  27.511759771141662,
+                            7.371968124588832,   14.434706651219557,  -20.905083235404213,
+                            15.072039322092493,  -1.996775358994629,  -19.848550070097502,
+                            16.3165181043086,    -25.318784739981893, 16.939771280984424,
+                            0.4214739129998719,  1.7633298382284686,  17.659133993124158 }, new TensorShape(2, 1, 5, 3));
+        var t = Tensor.FromArray<double>(
+            new double[] { 204.6451028610454,   -245.47588653769554, 254.63190599885453,
+                            -200.2528671581401,  -403.6714795350363,  -1037.0382604531853,
+                            -100.41452696082848, 444.1237337203991,   -50.28014887069747,
+                            -431.4138034439708,  -724.3654078010958,  -541.2932807875638,
+                            589.9744638696075,   -176.18769163944972, 297.82625554960487,
+                            385.34534307067946,  156.9094807871908,   54.160710580502084,
+                            773.7840651829667,   240.93324612115197,  193.01335037035483,
+                            -659.1048349134455,  29.6611816557244,    394.1328403132241,
+                            -517.5626502113245,  265.3783803432641,   -293.5803890561428,
+                            760.5509890823854,   83.63747341335062,   864.7565393290832,
+                            -210.53965883118147, 95.11606099496832,   234.54317446122678,
+                            431.2824832884677,   688.4052408516627,   580.9304146282878,
+                            -77.97495038431742,  -180.6052157399127,  -476.1827779964323,
+                            837.9928054550828,   -250.11800094461043, 436.1724309669594,
+                            -107.8276518256366,  260.74840560115484,  613.8562889468081,
+                            537.1959884794262,   211.51028931720717,  -408.54726433809026,
+                            -87.18820454463716,  -127.97879119314145, -167.37635674737132,
+                            -81.46814937971412,  298.7043152876581,   353.29081716454954,
+                            -124.1823264750605,  93.83775142666822,   605.7516461751454,
+                            -339.4930837885292,  325.461544706333,    -531.0002459790555,
+                            126.74511440221798,  316.74615601885375,  709.4434825820621,
+                            -597.8253632135093,  -247.82132339109492, -704.737830027385,
+                            -201.36741048315838, -105.36851711908248, -275.5465735989572,
+                            -539.9063120367825,  -649.9585535562818,  135.97787257080606 }, new TensorShape(2, 3, 4, 3));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test4()
+    {
+        var a = Tensor.FromArray<double>(new double[] { 13.801236798018898, 0.20150833845125504, 4.106608693856746 }, new TensorShape(3));
+        var b = Tensor.FromArray<double>(
+            new double[] { -6.029940749041376, 20.753970138217923, -8.327858181118444,
+                            -5.188661244672758, -9.050311859739129, 25.823909953964076,
+                            24.2141803580204,   7.639806832295562,  22.544197020059514,
+                            4.919069105496106,  2.2853523605274546, 24.35300450129494,
+                            -22.79961732500386, 15.6879441785144,   -15.475480302585678,
+                            -7.532337394669419, -21.94777113362987, -29.979519975059393,
+                            27.491684686351228, 26.58821611324877,  7.474320930137196,
+                            -20.15631608790095, -4.565966677821834, -3.5342800710705866 }, new TensorShape(2, 2, 3, 2));
+        var t = Tensor.FromArray<double>(
+            new double[] { -122.06486238579787, 391.4335909966807, 348.1135285464756,
+                            206.4382766323594, -407.9122636772442, 91.88104632149431,
+                            362.1747498694832, 348.3746955852936 }, new TensorShape(2, 2, 1, 2));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test5()
+    {
+        var a = Tensor.FromArray<double>(
+            new double[] { -6.863911328798498,  6.285950807000905,  -4.551999714397429,
+                            2.9442452537987336,  7.518547665868375,  12.767654916974834,
+                            -16.77109517963331,  3.498415848555169,  -18.854288913388295,
+                            13.669603055438088,  9.803970800255897,  -2.3831534529139695,
+                            16.426462170858507,  0.8752655525789024, -3.34227609300401,
+                            -10.513158897654233, -7.516722402630762, 9.963182036346243,
+                            18.06872709287783,   9.18873334694911,   18.883364891458918,
+                            -9.276501048623139,  -6.907303739816237, -18.57553847557326 }, new TensorShape(3, 1, 2, 4));
+        var b = Tensor.FromArray<double>(new double[] { 7.893349570547649, 20.824402386134224,
+                                                           12.672536168019768, 18.002556060079307 }, new TensorShape(4));
+        var t = Tensor.FromArray<double>(
+            new double[] { 72.04047666360029, 175.67342570179252, 217.1761421082951,
+                           -83.73215691205462, 542.5424790165914, -466.06481963441973 }, new TensorShape(3, 1, 2, 1));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test6()
+    {
+        var a = Tensor.FromArray<double>(new double[] { 99.89 }, new TensorShape(1, 1));
+        var b = Tensor.FromArray<float>(new float[] { 65.32f }, new TensorShape(1, 1, 1));
+        var t = Tensor.FromArray<double>(new double[] { 6524.8148 }, new TensorShape(1, 1, 1));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p, 1e-4));
+    }
+
+    [Test]
+    public void Test7()
+    {
+        var a = Tensor.FromArray<double>(new double[] { 99.89 }, new TensorShape(1, 1, 1, 1));
+        var b = Tensor.FromArray<float>(new float[] { 65.32f }, new TensorShape(1));
+        var t = Tensor.FromArray<double>(new double[] { 6524.8148 }, new TensorShape(1, 1, 1, 1));
+        var p = a.Matmul(b);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p, 1e-4));
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/Permute.cs b/csharp/TensorOpTest/Permute.cs
new file mode 100644
index 0000000..2fb1aff
--- /dev/null
+++ b/csharp/TensorOpTest/Permute.cs
@@ -0,0 +1,57 @@
+using NUnit.Framework;
+using Tensornet;
+
+namespace TensorOpTest;
+
+public class PermuteTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var s = Tensor.FromArray<int>(
+            new int[] { -19, -5, -13, 17,  -19, -19, 8, 4,   -4, -11, -5,  18,
+                        4,   -7, -13, -17, 4,   4,   6, -12, -5, 5,   -16, -15 }, new TensorShape(3, 2, 4));
+        var t = Tensor.FromArray<int>(
+            new int[] { -19, -4, 4,  -5,  -11, 4, -13, -5,  6,   17, 18,  -12,
+                        -19, 4,  -5, -19, -7,  5, 8,   -13, -16, 4,  -17, -15 }, new TensorShape(2, 4, 3));
+        var p = s.Permute(1, 2, 0);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test2()
+    {
+        var s = Tensor.FromArray<int>(
+            new int[] { -151, -46,  -9,   -62,  -158, -74,  35,   -10,  -123, -94,  -122, 58,
+                        -124, 139,  -173, -137, -178, 116,  52,   -92,  -14,  -176, -133, -109,
+                        -114, -157, -186, 46,   -78,  -144, 155,  -60,  47,   150,  -133, -58,
+                        -17,  -161, -36,  11,   133,  -170, -149, -155, 10,   -118, -112, -103,
+                        -110, 183,  29,   21,   189,  -85,  83,   -186, -114, -104, -171, -116,
+                        -110, 88,   -130, 42,   106,  120,  -94,  -77,  49,   74,   96,   -28 }, new TensorShape(6, 3, 2, 2));
+        var t = Tensor.FromArray<int>(
+            new int[] { -151, -124, -114, -17,  -110, -110, -158, -178, -78,  133,  189,  106,
+                        -123, -14,  47,   10,   -114, 49,   -46,  139,  -157, -161, 183,  88,
+                        -74,  116,  -144, -170, -85,  120,  -94,  -176, 150,  -118, -104, 74,
+                        -9,   -173, -186, -36,  29,   -130, 35,   52,   155,  -149, 83,   -94,
+                        -122, -133, -133, -112, -171, 96,   -62,  -137, 46,   11,   21,   42,
+                        -10,  -92,  -60,  -155, -186, -77,  58,   -109, -58,  -103, -116, -28 }, new TensorShape(2, 2, 3, 6));
+        var p = s.Permute(2, 3, 1, 0);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test3()
+    {
+        var s = Tensor.FromArray<double>(
+            new double[] { -144.88911173213074, -177.9092558668978 }, new TensorShape(1, 2));
+        var t = Tensor.FromArray<double>(
+            new double[] { -144.88911173213074, -177.9092558668978 }, new TensorShape(2, 1));
+        var p = s.Permute(1, 0);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/Sum.cs b/csharp/TensorOpTest/Sum.cs
new file mode 100644
index 0000000..a580044
--- /dev/null
+++ b/csharp/TensorOpTest/Sum.cs
@@ -0,0 +1,60 @@
+using NUnit.Framework;
+using Tensornet;
+
+namespace TensorOpTest;
+
+public class SumTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var s = Tensor.FromArray<int>(
+            new int[] { 18, 4, 35, 22, 34, 18, 15, 5, 13, 49, 41, 3, 
+                        4, 35, 14, 22, 38, 29, 42, 47, 30, 4, 9, 0, 
+                        35, 0, 43, 27, 44, 29, 2, 17, 16, 6, 36, 41, 
+                        1, 22, 3, 30, 35, 13, 41, 45, 38, 44, 38, 21, 
+                        42, 41, 19, 24, 40, 39, 37, 24, 28, 17, 22, 21, 
+                        15, 23, 35, 16, 31, 3, 29, 28, 0, 15, 17, 10 }, new TensorShape(3, 4, 2, 3));
+        var t = Tensor.FromArray<int>(
+            new int[] { 1734 }, new TensorShape(1));
+        var p = s.Sum();
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    // [Test]
+    // public void Test2()
+    // {
+    //     var s = Tensor.FromArray<int>(
+    //         new int[] { -151, -46,  -9,   -62,  -158, -74,  35,   -10,  -123, -94,  -122, 58,
+    //                     -124, 139,  -173, -137, -178, 116,  52,   -92,  -14,  -176, -133, -109,
+    //                     -114, -157, -186, 46,   -78,  -144, 155,  -60,  47,   150,  -133, -58,
+    //                     -17,  -161, -36,  11,   133,  -170, -149, -155, 10,   -118, -112, -103,
+    //                     -110, 183,  29,   21,   189,  -85,  83,   -186, -114, -104, -171, -116,
+    //                     -110, 88,   -130, 42,   106,  120,  -94,  -77,  49,   74,   96,   -28 }, new TensorShape(6, 3, 2, 2));
+    //     var t = Tensor.FromArray<int>(
+    //         new int[] { -151, -124, -114, -17,  -110, -110, -158, -178, -78,  133,  189,  106,
+    //                     -123, -14,  47,   10,   -114, 49,   -46,  139,  -157, -161, 183,  88,
+    //                     -74,  116,  -144, -170, -85,  120,  -94,  -176, 150,  -118, -104, 74,
+    //                     -9,   -173, -186, -36,  29,   -130, 35,   52,   155,  -149, 83,   -94,
+    //                     -122, -133, -133, -112, -171, 96,   -62,  -137, 46,   11,   21,   42,
+    //                     -10,  -92,  -60,  -155, -186, -77,  58,   -109, -58,  -103, -116, -28 }, new TensorShape(2, 2, 3, 6));
+    //     var p = s.Sum(2, 3, 1, 0);
+    //     Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    // }
+
+    // [Test]
+    // public void Test3()
+    // {
+    //     var s = Tensor.FromArray<double>(
+    //         new double[] { -144.88911173213074, -177.9092558668978 }, new TensorShape(1, 2));
+    //     var t = Tensor.FromArray<double>(
+    //         new double[] { -144.88911173213074, -177.9092558668978 }, new TensorShape(2, 1));
+    //     var p = s.Sum(1, 0);
+    //     Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    // }
+}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/TensorOpTest.csproj b/csharp/TensorOpTest/TensorOpTest.csproj
new file mode 100644
index 0000000..59e3607
--- /dev/null
+++ b/csharp/TensorOpTest/TensorOpTest.csproj
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <Nullable>enable</Nullable>
+
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
+    <PackageReference Include="NUnit" Version="3.13.2" />
+    <PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
+    <PackageReference Include="coverlet.collector" Version="3.1.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Tensor.NET\Tensor.NET.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/csharp/TensorOpTest/Transpose.cs b/csharp/TensorOpTest/Transpose.cs
new file mode 100644
index 0000000..c4293a6
--- /dev/null
+++ b/csharp/TensorOpTest/Transpose.cs
@@ -0,0 +1,83 @@
+using NUnit.Framework;
+using Tensornet;
+
+namespace TensorOpTest;
+
+public class TransposeTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var s = Tensor.FromArray<int>(
+            new int[] { 116,  -50, 79,  -180, -28,  -96, -165, -27,  -2,  10,
+                        -176, 113, -30, 122,  -116, -51, -48,  -142, 198, -108 }, new TensorShape(4, 5));
+        var t = Tensor.FromArray<int>(
+            new int[] { 116, -96,  -176, -51, -50, -165, 113, -48, 79,   -27,
+                        -30, -142, -180, -2,  122, 198,  -28, 10,  -116, -108 }, new TensorShape(5, 4));
+        var p = s.Transpose(0, 1);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test2()
+    {
+        var s = Tensor.FromArray<int>(
+            new int[] { -149, 141, 6,    -5,   47,   -183, 95,  53,   -149, -129, 186,  99,
+                        21,   84,  -14,  175,  -53,  62,   140, 59,   -14,  50,   -169, -189,
+                        61,   -7,  129,  -125, 120,  -109, 118, 162,  -80,  58,   -91,  115,
+                        127,  173, -140, -95,  0,    192,  66,  -83,  -188, -29,  -92,  -145,
+                        -6,   6,   -149, 159,  155,  93,   -74, -147, -37,  182,  123,  -49,
+                        112,  -23, -157, -73,  -182, -120, -45, 31,   -119, -2,   18,   -199 }, new TensorShape(3, 4, 6));
+        var t = Tensor.FromArray<int>(
+            new int[] { -149, 61,   -6,   95,   118, -74,  21,  127,  112,  140,  66,   -45,
+                        141,  -7,   6,    53,   162, -147, 84,  173,  -23,  59,   -83,  31,
+                        6,    129,  -149, -149, -80, -37,  -14, -140, -157, -14,  -188, -119,
+                        -5,   -125, 159,  -129, 58,  182,  175, -95,  -73,  50,   -29,  -2,
+                        47,   120,  155,  186,  -91, 123,  -53, 0,    -182, -169, -92,  18,
+                        -183, -109, 93,   99,   115, -49,  62,  192,  -120, -189, -145, -199 }, new TensorShape(6, 4, 3));
+        var p = s.Transpose(0, 2);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+
+    [Test]
+    public void Test3()
+    {
+        var s = Tensor.FromArray<double>(
+            new double[] { -184.47836822007747, -135.77934833640774, -131.5692045780147,
+                            -139.88637591883548, 7.378481161197385,   -194.85200685695676,
+                            124.72624465928914,  135.32088057645922,  -149.63309464031306,
+                            5.068530645297187,   -52.609905261884194, -5.83477677509191,
+                            -121.05109868593935, -133.2874615589557,  -108.08990561152387,
+                            26.347047463464122,  -65.70184395799768,  97.66633377665073,
+                            68.78419424070898,   181.3987014174778,   -17.19642705834329,
+                            -177.23101755020787, 126.42885351936985,  77.86682388216076,
+                            -91.77207240374065,  -195.63750250530325, -139.38054574551325,
+                            -150.04838481717218, -166.67773021921147, -93.0514518842402,
+                            -157.03624051832935, 60.03739681630253,   -184.79994903860072,
+                            20.311576166940227,  -15.716149646852216, 147.9009927690924,
+                            152.8980211985887,   -195.4208022280849,  98.5139649928654,
+                            16.655507860783615 }, new TensorShape(2, 1, 4, 5));
+        var t = Tensor.FromArray<double>(
+            new double[] { -184.47836822007747, -17.19642705834329,  -135.77934833640774,
+                            -177.23101755020787, -131.5692045780147,  126.42885351936985,
+                            -139.88637591883548, 77.86682388216076,   7.378481161197385,
+                            -91.77207240374065,  -194.85200685695676, -195.63750250530325,
+                            124.72624465928914,  -139.38054574551325, 135.32088057645922,
+                            -150.04838481717218, -149.63309464031306, -166.67773021921147,
+                            5.068530645297187,   -93.0514518842402,   -52.609905261884194,
+                            -157.03624051832935, -5.83477677509191,   60.03739681630253,
+                            -121.05109868593935, -184.79994903860072, -133.2874615589557,
+                            20.311576166940227,  -108.08990561152387, -15.716149646852216,
+                            26.347047463464122,  147.9009927690924,   -65.70184395799768,
+                            152.8980211985887,   97.66633377665073,   -195.4208022280849,
+                            68.78419424070898,   98.5139649928654,    181.3987014174778,
+                            16.655507860783615 }, new TensorShape(4, 1, 5, 2));
+        var p = s.Transpose(0, 2).Transpose(2, 3);
+        Assert.IsTrue(TensorUtils.IsValueEqual(t, p));
+    }
+}
\ No newline at end of file
diff --git a/csharp/TensorOpTest/TypeConvert.cs b/csharp/TensorOpTest/TypeConvert.cs
new file mode 100644
index 0000000..8f4bbaf
--- /dev/null
+++ b/csharp/TensorOpTest/TypeConvert.cs
@@ -0,0 +1,48 @@
+using NUnit.Framework;
+using Tensornet;
+using System;
+
+namespace TensorOpTest;
+
+public class TypeConvertTest
+{
+    [SetUp]
+    public void Setup()
+    {
+    }
+
+    [Test]
+    public void Test1()
+    {
+        var inp = Tensor.FromArray<double>(
+                new double[]{-184.47836822007747, -135.77934833640774, -131.5692045780147,
+                            -139.88637591883548, 7.378481161197385,   -194.85200685695676,
+                            124.72624465928914,  135.32088057645922,  -149.63309464031306,
+                            5.068530645297187,   -52.609905261884194, -5.83477677509191,
+                            -121.05109868593935, -133.2874615589557,  -108.08990561152387,
+                            26.347047463464122,  -65.70184395799768,  97.66633377665073,
+                            68.78419424070898,   181.3987014174778,   -17.19642705834329,
+                            -177.23101755020787, -157.03624051832935, 16.655507860783615}, 
+                            new int[]{2, 3, 4})[0..2, 1..3, 2..4];
+        var pred = inp.ToTensor<int>();
+        var truth = Tensor.FromArray<int>(new int[] { 124, 135, -52, -5, 68, 181, -157, 16 }, new int[] { 2, 2, 2 });
+        Assert.IsTrue(TensorUtils.IsValueEqual(pred, truth));
+    }
+    [Test]
+    public void Test2()
+    {
+        var inp = Tensor.FromArray<double>(
+                new double[]{-184.47836822007747, -135.77934833640774, -131.5692045780147,
+                            -139.88637591883548, 7.378481161197385,   -194.85200685695676,
+                            .0,  135.32088057645922,  -149.63309464031306,
+                            5.068530645297187,   -52.609905261884194, -5.83477677509191,
+                            -121.05109868593935, -133.2874615589557,  -108.08990561152387,
+                            26.347047463464122,  -65.70184395799768,  97.66633377665073,
+                            68.78419424070898,   181.3987014174778,   -17.19642705834329,
+                            -177.23101755020787, -157.03624051832935, .0}, 
+                            new int[]{2, 3, 4})[0..2, 1..3, 2..4];
+        var pred = inp.ToTensor<bool>();
+        var truth = Tensor.FromArray<bool>(new bool[] { false, true, true, true, true, true, true, false }, new int[] { 2, 2, 2 });
+        Assert.IsTrue(TensorUtils.IsValueEqual(pred, truth));
+    }
+}
\ No newline at end of file
diff --git a/doc/API_Mapping.md b/doc/API_Mapping.md
new file mode 100644
index 0000000..e69de29
diff --git a/doc/Build.md b/doc/Build.md
new file mode 100644
index 0000000..cc12269
--- /dev/null
+++ b/doc/Build.md
@@ -0,0 +1,37 @@
+## Cmake build on Windows
+
+1. Install ```mingw-w64``` and set the enviroment variable.
+2. clone the project.
+3. use the following command
+
+```bash
+$ mkdir build
+$ cd build
+$ cmake .. -G "MinGW Makefiles"
+$ mingw32-make
+```
+    
+## Cmake build on Linux
+
+1. clone the project.
+2. use the following command
+
+```bash
+$ mkdir build
+$ cd build
+$ cmake ..
+$ make
+```
+
+## Cmake cross-build on Linux targeting windows
+
+1. Install ```mingw-w64``` and set the enviroment variable.
+2. clone the project.
+3. use the following command
+
+```bash
+$ mkdir build
+$ cd build
+$ cmake .. -DNN_CROSS_COMPILE=ON
+$ make
+```
diff --git a/doc/Readme-zh-cn.md b/doc/Readme-zh-cn.md
new file mode 100644
index 0000000..564fc84
--- /dev/null
+++ b/doc/Readme-zh-cn.md
@@ -0,0 +1,129 @@
+中文 | [English](../)
+
+# Tensor.NET
+
+Tensor.NET是一个轻量级、高性能的.NET平台下的张量运算库，用c++作为底层实现，提供与Numpy相似的功能，但是封装成.NET风格的接口，使用泛型张量，支持Linq接口，C#原生的slice方式等等。
+
+## Tensor.NET的优势
+
+当前已经有了一些张量计算相关的.NET库，比如[NumSharp](https://github.com/SciSharp/NumSharp)和[TensorFlow.NET](https://github.com/SciSharp/TensorFlow.NET)。作者也从这两个库中学习到了很多，在此向这两个库的作者们表示感谢！
+
+然而，本项目创建的原因便是，这两个库多少在某些方面很优秀，在另外一些方面存在不足，TensorFlow.NET是一个非常优秀的.NET深度学习库，但是对于普通的张量运算来讲太过庞大了；NumSharp实现了Numpy风格的接口，对Python直接向.NET转换比较友好，但是底层实现的效率不高，且对泛型的擦除使得一些功能在强类型语言下受限。
+
+本项目的特性具体如下：
+
+- **更快的运算速度**
+
+    Tensor.NET采用C++作为底层实现，然后使用C#与其进行Interop并提供封装，后续会逐步优化各个算子的实现来提速。
+
+- **泛型张量**
+
+    本项目的设计理念与```System.Numerics.Tensor```比较相似，采用泛型类而非类型擦除，与其它大多数情况下的.NET特性保持一致，这样更方便进行扩展，也对未接触过Numpy等弱类型语言张量框架的开发者友好一些。
+
+- **.NET风格的API**
+
+    本项目提供Numpy库中的主要功能部分，但是并不封装成Python风格的API，而是采用.NET风格，例如```Tensor<T>```继承并实现了```IEnumerable<T>```接口，可以直接用foreach迭代、转换成List、使用Linq接口等等，支持C#原生的切片方式，比如```t[..3, 2..^1]```。
+
+在未来，本项目计划提供以下特性：
+
+- 针对x86平台进行算子优化
+
+- 支持多线程计算
+
+- 支持在图像、Excel、CSV等文件中读写张量
+
+## 安装方式
+
+使用如下命令
+
+```
+PM> Install-Package Tensor.NET
+```
+或
+```
+$ dotnet add package Tensor.NET
+```
+也可以在VS的Nuget包管理器中搜索Tensor.NET并安装。
+
+## 文档
+
+- 关于如何构建c++库，请参考[此文档](./Build.md)
+
+- 关于Tensor.NET的例程，请参考[此项目](https://github.com/AsakusaRinne/Tensor.NET.Examples)
+
+## Benchmarks (制作中)
+
+后续会加入基准测试
+
+## 简单的使用
+
+```cs
+using Tensornet;
+using Tensornet.Common;
+using Tensornet.Math;
+using System.Text;
+
+// Matrix multiply
+// Create Tensor from arrays.
+Tensor<int> a = Tensor.FromArray<int>(new int[]{1, 2, 3, 4, 5, 6}, new int[] { 1, 3, 2 });
+Tensor<double> b = Tensor.FromArray<double>(new double[]{1.2, 2.6, 3.9, 4.1, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6, 1.2, 2.6, 
+                    3.9, 4.1, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6, 5.0, 6.5, 1.7, 2, 3, 4, 5, 6}, new int[] { 4, 2, 4 });
+var c = a.Matmul(b);
+// Print the tensor
+Console.WriteLine(c);
+
+// Reshape, permute and transpose
+c = c.Reshape(new int[] { 6, 8 }).Permute(new int[] { 1, 0 }).Transpose(0, 1);
+
+// Indexer
+c[0, 0] = 999.0;
+c[5, 7] = -888.0;
+
+// Slice
+var s = c[1..3, ^5..^2];
+
+
+// Random generations
+var gaussian = Tensor.Random.Normal<int>(new TensorShape(5, 6), 0, 5);
+var avg = Tensor.Random.Uniform<int>(new TensorShape(5, 6), -5, 5);
+
+// Other creation methods for Tensor
+var eye = Tensor.Eye<int>(4, 5, 2);
+eye.Fill(258);
+var linspace = Tensor.Linspace<double>(0, 10, 5);
+var ones = Tensor.Ones<int>(new int[] { 2, 3 });
+
+// Squeeze and unsqueeze
+ones = ones.Unsqueeze(1);
+ones = ones.Unsqueeze(3);
+ones = ones.Squeeze();
+
+// Using Linq APIs
+foreach(var item in eye){
+    Console.WriteLine($"{item} ");
+}
+
+// Bool index
+var bidx = Tensor.Random.Uniform<bool>(new int[] { 3, 3 }, 0, 1);
+var bres = ta[ta > 20];
+var twhere = ta.Where(x => x > 20, 999, -555);
+var trefer = Tensor.Random.Uniform<int>(new int[] { 2, 3, 3 }, -100, -50);
+ta[ta > 20] = trefer;
+
+// Serialization and Deserialization
+TensorWriter.Write("test.tn", ta);
+var tr = TensorReader.Read<float>("test.tn");
+
+// argmax and argmin
+var src = Tensor.Random.Uniform<int>(new int[] { 4, 5 }, 0, 50);
+var amax = src.Argmax(0);
+var amin = src.Argmin(1);
+```
+
+## 主要的缺陷
+
+虽然Tensor.NET在某些方面有优点，但不可否认其有一些明显的缺点，最主要的缺点就是基础运算符，因为.NET6和C# 10目前不支持运算符的泛型定义，也不支持针对运算符进行扩展方法那样的泛型方法特化，所以无法对加减乘除以及取模这几个基础运算符支持所有类型张量之间的操作。
+
+当前只能在同类型张量之间使用这些基础运算符，或者使用```Add```, ```Div```等函数接口来进行不同类型张量之间的运算，这可能会带来一些不方便。
+
+这一点有望在.NET7正式发布后得到解决，利用其新特性对该问题进行优化。
diff --git a/doc/TODO.md b/doc/TODO.md
new file mode 100644
index 0000000..ab714e0
--- /dev/null
+++ b/doc/TODO.md
@@ -0,0 +1,61 @@
+## Num.NET
+
+### TODO
+
+| Event | Description | Priority | Status |
+| ----- | --------------| --- | ----- |
+| Complete the smallest framework of c++ part | Construct a smallest framework of c++ part which could run naive implemention of Matmul successfully. In this stage, extensibility should be taken into consideration, but details could be ignored. | p0 | Complete ✅ |
+| Complete naive implemention of Matmul | The results of naive implemention of matmul are wrong. Besides, more test cases need to be added, including more shapes, more dtypes and more data. | p0 | Complete ✅ |
+| Add Benchmark test for Matmul | Add some bechmark tests for Matmul to evaluate its effeciency | p1 | Waiting 🔵 |
+| Add deduce to decide the output shape of ops | Checking if the shapes are matched in the body of op is not a good choice. A calculator for it is needed | p0 | Complete ✅ |
+| Add broadcast | Add broadcast with stride and wrap it | p0 | Complete ✅ |
+| Add script to auto build and test | Write a script on linux to build and run all tests automatically | p2 | Waiting 🔵 |
+| Add ```reshape``` | Add ```reshape``` for ```NDArray``` | p0 | Complete ✅ |
+| Add naive op ```dot``` | Add naive implementation for op ```dot``` | p0 | Complete ✅ |
+| Add naive op ```transpose``` | Add naive implementation for op ```transpose``` | p0 | Complete ✅ |
+| Add naive op ```permute``` | Add naive implementation for op ```permute``` | p0 | Complete ✅ |
+| Add naive op ```add``` | Add naive implementation for op ```add``` | p0 | Complete ✅ |
+| Add naive op ```sub``` | Add naive implementation for op ```sub``` | p0 | Complete ✅ |
+| Add naive op ```mul``` | Add naive implementation for op ```mul``` | p0 | Complete ✅ |
+| Add naive op ```div``` | Add naive implementation for op ```div``` | p0 | Complete ✅ |
+| Add ```type_deduce``` | Add deduce method for type of layout to decide the ```dtype``` of the output Array. | p0 | Complete ✅ |
+| Add ```Status``` | Add a status struct to tell the caller if the call success and return error message if failed. | p0 | Complete ✅ |
+| Define actions on Debug and Release mode | define different actions for one expr on different mode. For instance, ```nn_assert``` should not take action on release mode | p0 | Waiting 🔵 |
+| Add ```Checker``` for test | Add a checker class to judge if the pred and result are matched. | p0 | On going 🚀 |
+| Add Process for overflow | Add process to deal with overflow, show user NAN instead of completely wrong data. | p0 | Waiting 🔵 |
+| Add ```slice``` | Add support of ```slice``` | p0 | Complete ✅ |
+| Add CSharp interop | Test CSharp interop, with a few basic apis | p0 | Complete ✅ |
+| Add Serialization for .npy file | Provide interface to serialize and deserialize with .npy files | p0 | Waiting 🔵 |
+| Support reading of excel | Provide interface to read and write with .csv and excel file | p1 | Waiting 🔵 |
+| Support type convert | Support type convert of ```Tensor``` | p0 | On going 🚀 |
+| Type deduce for double-input ops | Add type deduce of double-input ops in c++ part | p2 | Complete ✅ |
+| Add self-manipulated type ops | Except for single input and double input ops, there are some ops directly manipulating itself's data, such as IDentity, Ones and so on. | p0 | Complete ✅ |
+| Type deduce in C# part | Add type deduce of C# part | p0 | Complete ✅ |
+| Design exceptions in C# part | Design exceptions in C# part | p0 | Complete ✅ |
+| Add unit tests for C# part | Add unit tests for C# part | p0 | Complete ✅ |
+| Add squeeze method | Add squeeze method in C#. | p0 | Complete ✅ |
+| Add unsqueeze method | Add unsqueeze method in C#. | p0 | Complete ✅ |
+| Add concat method | Add squeeze method and op. | p0 | Complete ✅ |
+| Use attributes to support multithread of ops is a good idea. | idea | p0 | Waiting 🔵 |
+| Use attributes to control the provider. | idea | p0 | Waiting 🔵 |
+| Use c++20 format in macros   | idea | p1 | Waiting 🔵 |
+| Add flip op |  | p0 | Complete ✅ |
+| Add ratate op |  | p0 | Complete ✅ |
+| Add argwhere op |  | p1 | Waiting 🔵 |
+| Add argmax op |  | p0 | Complete ✅ |
+| Add argmin op |  | p0 | Complete ✅ |
+| Add matrix inverse op |  | p0 | Complete ✅ |
+| Add mesh indexing op |  | p2 | Waiting 🔵 |
+| Add onehot op |  | p0 | Complete ✅ |
+| Add padding op |  | p0 | Partly complete🚀 |
+| Add repeat op |  | p0 | Complete ✅ |
+| Add sort op |  | p0 | Complete ✅ |
+| Add topk op |  | p2 | Waiting 🔵 |
+| Add arrange op |  | p0 | Complete ✅ |
+| Add zeros_like, ones_like, fill_like methods |  | p0 | Complete ✅ |
+| Add flatten method |  | p0 | Complete ✅ |
+| Add max, min, mean op |  | p0 | Complete ✅ |
+| Add mod, and, or, xor operators in csharp |  | p0 | Complete ✅ |
+
+
+✅   ❌   🚀   🔵
diff --git a/tools/scripts/cross_build.sh b/tools/scripts/cross_build.sh
new file mode 100644
index 0000000..b8b0274
--- /dev/null
+++ b/tools/scripts/cross_build.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+set -e
+
+BUILD_TYPE=Release
+NN_WITH_TEST=OFF
+MAX_PARALLEL_JOBS=0
+
+cpu_number=`nproc`
+package_name=libnumnet
+output_dir=./output
+build_dir=./build/Linux/$BUILD_TYPE
+
+function config_default_max_jobs() {
+    if [[ $OS =~ "NT" ]]; then
+        ((MAX_PARALLEL_JOBS = ${cpu_number} - 1))
+        if [[ ${MAX_PARALLEL_JOBS} -le 0 ]]; then
+            MAX_PARALLEL_JOBS=1
+        fi
+    else
+        ((MAX_PARALLEL_JOBS = ${cpu_number} + 2))
+    fi
+    echo "config default MAX_PARALLEL_JOBS to ${MAX_PARALLEL_JOBS} [cpu number is:${cpu_number}]"
+}
+
+config_default_max_jobs
+echo "EXTRA_CMAKE_ARGS: ${EXTRA_CMAKE_ARGS}"
+
+function usage() {
+    echo "$0 args1 args2 .."
+    echo "available args detail:"
+    echo "-d : Build with Debug mode, default Release mode"
+    echo "-t : Build with test, default without test"
+    echo "-j : run N jobs in parallel for ninja, defaut is cpu_number + 2"
+    echo "-h : show usage"
+    echo "append other cmake config by config EXTRA_CMAKE_ARGS"
+    exit -1
+}
+
+while getopts "lnsrhdcmve:j:" arg
+do
+    case $arg in
+        j)
+            MAX_PARALLEL_JOBS=$OPTARG
+            echo "config MAX_PARALLEL_JOBS to ${MAX_PARALLEL_JOBS}"
+            ;;
+        d)
+            echo "Build with Debug mode"
+            BUILD_TYPE=Debug
+            ;;
+        t)
+            echo "Build with test"
+            NN_WITH_TEST=ON
+            ;;
+        h)
+            echo "show usage"
+            usage
+            ;;
+        ?)
+            echo "unkonw argument"
+            usage
+            ;;
+    esac
+done
+echo "------------------------------------"
+echo "build config summary:"
+echo "BUILD_TYPE: $BUILD_TYPE"
+echo "NN_WITH_TEST: $NN_WITH_TEST"
+echo "MAX_PARALLEL_JOBS: $MAX_PARALLEL_JOBS"
+echo "------------------------------------"
+
+function cmake_build() {
+    build_dir=./build/Windows/$BUILD_TYPE
+    # fork a new bash to handle EXTRA_CMAKE_ARGS env with space
+    bash -c "rm -rf ${build_dir}"
+    mkdir -p $build_dir
+    cd "build"
+    bash -c "cmake .. -DNN_WITH_TEST=$NN_WITH_TEST -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DNN_CROSS_COMPILE=ON ${EXTRA_CMAKE_ARGS}"
+    bash -c "make -j${MAX_PARALLEL_JOBS}"
+    cd ".."
+    bash -c "cp ${build_dir}/lib/${package_name}.dll ${output_dir}/${package_name}.dll"
+    echo "copy the library file to output directory."
+}
+
+cmake_build
\ No newline at end of file
diff --git a/tools/scripts/host_build.sh b/tools/scripts/host_build.sh
new file mode 100644
index 0000000..ac9fb4e
--- /dev/null
+++ b/tools/scripts/host_build.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+set -e
+
+BUILD_TYPE=Release
+NN_WITH_TEST=OFF
+MAX_PARALLEL_JOBS=0
+
+cpu_number=`nproc`
+package_name=libnumnet
+output_dir=./output
+build_dir=./build/Linux/$BUILD_TYPE
+
+function config_default_max_jobs() {
+    if [[ $OS =~ "NT" ]]; then
+        ((MAX_PARALLEL_JOBS = ${cpu_number} - 1))
+        if [[ ${MAX_PARALLEL_JOBS} -le 0 ]]; then
+            MAX_PARALLEL_JOBS=1
+        fi
+    else
+        ((MAX_PARALLEL_JOBS = ${cpu_number} + 2))
+    fi
+    echo "config default MAX_PARALLEL_JOBS to ${MAX_PARALLEL_JOBS} [cpu number is:${cpu_number}]"
+}
+
+config_default_max_jobs
+echo "EXTRA_CMAKE_ARGS: ${EXTRA_CMAKE_ARGS}"
+
+function usage() {
+    echo "$0 args1 args2 .."
+    echo "available args detail:"
+    echo "-d : Build with Debug mode, default Release mode"
+    echo "-t : Build with test, default without test"
+    echo "-j : run N jobs in parallel for ninja, defaut is cpu_number + 2"
+    echo "-h : show usage"
+    echo "append other cmake config by config EXTRA_CMAKE_ARGS"
+    exit -1
+}
+
+while getopts "lnsrhdcmve:j:" arg
+do
+    case $arg in
+        j)
+            MAX_PARALLEL_JOBS=$OPTARG
+            echo "config MAX_PARALLEL_JOBS to ${MAX_PARALLEL_JOBS}"
+            ;;
+        d)
+            echo "Build with Debug mode"
+            BUILD_TYPE=Debug
+            ;;
+        t)
+            echo "Build with test"
+            NN_WITH_TEST=ON
+            ;;
+        h)
+            echo "show usage"
+            usage
+            ;;
+        ?)
+            echo "unkonw argument"
+            usage
+            ;;
+    esac
+done
+echo "------------------------------------"
+echo "build config summary:"
+echo "BUILD_TYPE: $BUILD_TYPE"
+echo "NN_WITH_TEST: $NN_WITH_TEST"
+echo "MAX_PARALLEL_JOBS: $MAX_PARALLEL_JOBS"
+echo "------------------------------------"
+
+function cmake_build() {
+    build_dir=./build/Linux/$BUILD_TYPE
+    # fork a new bash to handle EXTRA_CMAKE_ARGS env with space
+    bash -c "rm -rf ${build_dir}"
+    mkdir -p $build_dir
+    cd "build"
+    bash -c "cmake .. -DNN_WITH_TEST=$NN_WITH_TEST -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DNN_CROSS_COMPILE=OFF ${EXTRA_CMAKE_ARGS}"
+    bash -c "make -j${MAX_PARALLEL_JOBS}"
+    cd ".."
+    bash -c "cp ${build_dir}/lib/${package_name}.so ${output_dir}/${package_name}.so"
+    echo "copy the library file to output directory."
+}
+
+cmake_build
\ No newline at end of file
