             "but got " ~ndims " instead"))))
 
 (defmacro with-check-shape [ndims [m-sym m] & body]
+  "Binds m-sym to m, with a type hinting indicating NDArray. Executes body
+  after running with-check-ndims and with-check-indices. Note that
+  body will have to adhere to the format expected by with-check-indices.
+  See (source with-check-indices) for more details
+  
+  Usage:
+  
+  (with-check-shape 2 [m (mp/construct-matrix (NDArray.) [ [1 2 3]])] row col
+    (println (.get m row col)))
+  
+  Note that explicit type hinting is not required if row and col are symbols,
+  this macro will automatically cast 'row and 'col to long types"
   `(let [~(with-meta m-sym {:tag NDArray}) ~m]
      (with-check-ndims ~ndims ~m-sym
        (with-check-indices ~ndims ~m-sym

             "\n"))))))

(defmacro with-check-ndims [ndims m & body]
  `(if (= ~ndims (.dimensionality ~m))
     (do ~@body)
     (error

        (aget shape dimension-number)))))

(defn- all?
  "returns true if every element in coll (or the mapped version, if 
  a map function is provided) is true. Truthyness is tested using true?, so
  only the Bolean true will be tested as positive."
  ([coll]
   (every? true? coll))
  ([f coll]
   (every? true? (mapv f coll))))

(defn- make-index-type-hints [syms]
  "Make index type hints.
  eg: Converts the list
  
  '(a b c d)
  
  into
  
  '[a (long a) b (long b) c (long c)]
  
  which can be used together with other bindings (or even on its own)
  in macros"
  (vec
    (mapcat (fn [sym]
              (if (symbol? sym)
                `[~sym (long ~sym)]
                `[]))
            syms)))

(defn- in-bound?
  "Returns true if the matrix is in bound, i.e:
  0 <= idx < len" 
  [^long idx ^long len]
  (and (>= idx 0) (< idx len)))

(defmacro with-check-indices
  "
  Check the indices (third or more arguments) based on the provided
  matrix (second argument). Throws an error if it is out of bound.

  The way the third and more arguments is interpreted depends on the
  symbol ndims. If it is a number (note, not a symbol that points to
  a number) and is equals to 1 , then the third argument will be
  the row number. If it is equals to 2, then third argument will
  be the row number and fourth argument will be the col number.
  Otherwise, the third number is treated as an array of indices.
  
  In all cases, the other arguments will be treated as the body. 

  This macro also does some autocasting for certain symbols (only for 
  ndims = 1 or 2). For eg, if the row / col number is a symbol rather
  than a number, then the symbol will be casted to a long explicitly,
  preventing the usage of the reflection API where possible.

  usage:
  (with-check-indices 1 m row
    (println (.get m row)))

  (with-check-indices 2 m row col
    (println (.get m row)))

  ; When the first argument (ndims) is not 1 or 2 
  (with-check-indices (inc 0) m [row]
    (println (.get m row)))
  
  (with-check-ndims 3 m [i j k]
    (println (.get m i j k))) 
  " 
  [ndims m & args]
   (let [row-sym (gensym "row_")
         col-sym (gensym "col_")
         indexes-sym (gensym "indexes_")
