// Path: src/main/java/org/oscii/math/VectorMath.java
// public final class VectorMath {
// 
//     private VectorMath() {
//     }
// 
//     /**
//      * Cosine similarity. Undefined for zero vectors.
//      *
//      * @param v1
//      * @param v2
//      * @return
//      */
//     public static double cosineSimilarity(float[] v1, float[] v2) {
//         if (v1.length != v2.length) throw new IllegalArgumentException();
//         double v1Dotv2 = 0.0;
//         double v1SS = 0.0;
//         double v2SS = 0.0;
//         for (int i = 0; i < v1.length; i++) {
//             v1Dotv2 += v1[i] * v2[i];
//             v1SS += v1[i] * v1[i];
//             v2SS += v2[i] * v2[i];
//         }
//         if (v1SS == 0.0f || v2SS == 0.0f)
//             throw new IllegalArgumentException("Cosine similarity undefined for zero vectors");
//         return v1Dotv2 / (Math.sqrt(v1SS) * Math.sqrt(v2SS));
//     }
// 
//     public static double cosineSimilarity(Vector v1, Vector v2) {
//         if (v1.size() != v2.size()) throw new IllegalArgumentException();
//         double v1v2 = v1.dot(v2);
//         double v1Norm = v1.norm(Vector.Norm.Two);
//         double v2Norm = v2.norm(Vector.Norm.Two);
//         if (v1Norm == 0.0 || v2Norm == 0.0)
//             throw new IllegalArgumentException("Cosine similarity undefined for zero vectors");
//         return v1v2 / (v1Norm * v2Norm);
//     }
// 
//     /**
//      * @param dest
//      * @param v1
//      */
//     public static void addInPlace(float[] dest, float[] v1) {
//         if (dest.length != v1.length) throw new IllegalArgumentException();
//         for (int i = 0; i < dest.length; ++i) dest[i] += v1[i];
//     }
// 
//     /**
//      * @param v1
//      * @param scalar
//      */
//     public static void multiplyInPlace(float[] v1, float scalar) {
//         if (scalar == 0.0f) throw new IllegalArgumentException();
//         for (int i = 0; i < v1.length; ++i) v1[i] *= scalar;
//     }
// 
// }


import com.eatthepath.jvptree.VPTree;
import no.uib.cipr.matrix.DenseVector;
import no.uib.cipr.matrix.Vector;
import org.oscii.math.VectorMath;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.LineNumberReader;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.stream.Collectors.toList;
     * @return
     * @throws IOException
     */
    public static EmbeddingContainer fromBinFile(File file, ByteOrder byteOrder, Set<String> vocab)
            throws IOException {
        return fromBinFile(file, byteOrder, vocab, false);
    }

    public static EmbeddingContainer fromBinFile(File file, ByteOrder byteOrder, Set<String> vocab, boolean doublePrec)
            throws IOException {
        try (final FileInputStream fis = new FileInputStream(file)) {
            final FileChannel channel = fis.getChannel();
            // TODO(spenceg) Simply open a binary file stream?
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
                    Math.min(channel.size(), Integer.MAX_VALUE));
            buffer.order(byteOrder);
            int bufferCount = 1;
            // Java's NIO only allows memory-mapping up to 2GB. To work around this problem, we re-map
            // every gigabyte. To calculate offsets correctly, we have to keep track how many gigabytes
            // we've already skipped. That's what this is for.

            StringBuilder sb = new StringBuilder();
            char c = (char) buffer.get();
            while (c != '\n') {
                sb.append(c);
                c = (char) buffer.get();
            }
            String firstLine = sb.toString();
            int index = firstLine.indexOf(' ');
            final int vocabSize = Integer.parseInt(firstLine.substring(0, index));
// Path: src/main/java/org/oscii/math/VectorMath.java
// public final class VectorMath {
// 
//     private VectorMath() {
//     }
// 
//     /**
//      * Cosine similarity. Undefined for zero vectors.
//      *
//      * @param v1
//      * @param v2
//      * @return
//      */
//     public static double cosineSimilarity(float[] v1, float[] v2) {
//         if (v1.length != v2.length) throw new IllegalArgumentException();
//         double v1Dotv2 = 0.0;
//         double v1SS = 0.0;
//         double v2SS = 0.0;
//         for (int i = 0; i < v1.length; i++) {
//             v1Dotv2 += v1[i] * v2[i];
//             v1SS += v1[i] * v1[i];
//             v2SS += v2[i] * v2[i];
//         }
//         if (v1SS == 0.0f || v2SS == 0.0f)
//             throw new IllegalArgumentException("Cosine similarity undefined for zero vectors");
//         return v1Dotv2 / (Math.sqrt(v1SS) * Math.sqrt(v2SS));
//     }
// 
//     public static double cosineSimilarity(Vector v1, Vector v2) {
//         if (v1.size() != v2.size()) throw new IllegalArgumentException();
//         double v1v2 = v1.dot(v2);
//         double v1Norm = v1.norm(Vector.Norm.Two);
//         double v2Norm = v2.norm(Vector.Norm.Two);
//         if (v1Norm == 0.0 || v2Norm == 0.0)
//             throw new IllegalArgumentException("Cosine similarity undefined for zero vectors");
//         return v1v2 / (v1Norm * v2Norm);
//     }
// 
//     /**
//      * @param dest
//      * @param v1
//      */
//     public static void addInPlace(float[] dest, float[] v1) {
//         if (dest.length != v1.length) throw new IllegalArgumentException();
//         for (int i = 0; i < dest.length; ++i) dest[i] += v1[i];
//     }
// 
//     /**
//      * @param v1
//      * @param scalar
//      */
//     public static void multiplyInPlace(float[] v1, float scalar) {
//         if (scalar == 0.0f) throw new IllegalArgumentException();
//         for (int i = 0; i < v1.length; ++i) v1[i] *= scalar;
//     }
// 
// }


// Path: src/main/java/org/oscii/neural/EmbeddingContainer.java
import com.eatthepath.jvptree.VPTree;
import no.uib.cipr.matrix.DenseVector;
import no.uib.cipr.matrix.Vector;
import org.oscii.math.VectorMath;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.LineNumberReader;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.stream.Collectors.toList;

     * @return
     * @throws IOException
     */
    public static EmbeddingContainer fromBinFile(File file, ByteOrder byteOrder, Set<String> vocab)
            throws IOException {
        return fromBinFile(file, byteOrder, vocab, false);
    }

    public static EmbeddingContainer fromBinFile(File file, ByteOrder byteOrder, Set<String> vocab, boolean doublePrec)
            throws IOException {
        try (final FileInputStream fis = new FileInputStream(file)) {
            final FileChannel channel = fis.getChannel();
            // TODO(spenceg) Simply open a binary file stream?
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
                    Math.min(channel.size(), Integer.MAX_VALUE));
            buffer.order(byteOrder);
            int bufferCount = 1;
            // Java's NIO only allows memory-mapping up to 2GB. To work around this problem, we re-map
            // every gigabyte. To calculate offsets correctly, we have to keep track how many gigabytes
            // we've already skipped. That's what this is for.

            StringBuilder sb = new StringBuilder();
            char c = (char) buffer.get();
            while (c != '\n') {
                sb.append(c);
                c = (char) buffer.get();
            }
            String firstLine = sb.toString();
            int index = firstLine.indexOf(' ');
            final int vocabSize = Integer.parseInt(firstLine.substring(0, index));
            final int layerSize = Integer.parseInt(firstLine.substring(index + 1));