1
votes

I am trying to get a range of card type from a list bins that are associated with that card type, but whenever I ran the code the card-type is printed as null am not sure what I am doing wrong enter image description here

This are the bins in a text

400000000000,499999999999,visa
500000000000,599999999999,mc
400000000000,499999999999,visa
420008000000,420008999999,visadebit
420008000000,435000999999,visa
540008000000,599999999999,mc

Whenever I Pass 4111111111111111 instead of getting visa i end getting null,

this is what I have done so far

package cardsystem;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;

public class Solution {

    /**
     * An entity to hold bin range details. A bin range is a pair of 12 digit numbers that
     * mark the boundaries of the range which is maped to other bin range properties such
     * as a card type. The range boundaries are inclusive.
     */
    static final class BinRange {
        final String start;
        final String end;
        final String cardType;

        BinRange(String start, String end, String cardType) {
            this.start = start;
            this.end = end;
            this.cardType = cardType;
        }
    }


    //  I need a hashmap which hold index and CardType. Each index corresponding an interval (start-end).
    private static HashMap<Integer, String> cache = new HashMap<>();
    private static List<Long> startEndList = new ArrayList<>();

    interface CardTypeCache {
        /**
         * @param cardNumber 12 to 23 digit card number.
         * @return the card type for this cardNumber or null if the card number does not
         * fall into any valid bin ranges.
         */
        String get(String cardNumber);
    }

    /**
     * @param binRanges the list of card bin ranges to build a cache from.
     * @return an implementation of CardTypeCache.
     */
    public static CardTypeCache buildCache(List<BinRange> binRanges) {

        cache = new HashMap<>();

        for (int i = 0; i < binRanges.size(); i++) {
            cache.put(i, binRanges.get(i).cardType);
        }

        for (BinRange binRange : binRanges) {
            startEndList.add(Long.valueOf(binRange.start)); // Previously, Integer type used, I converted it to 'Long' type
            startEndList.add(Long.valueOf(binRange.end));
        }

        return new CardTypeCacheImpl();
    }

    static class CardTypeCacheImpl implements CardTypeCache {


        public String get(String cardNumber) {

            //Integer index = findIndexLinear(Long.valueOf(cardNumber));    // Linear Search
            Integer index = findIndexBinary(Long.valueOf(cardNumber));      // Binary Search
            return (index != -1) ? cache.get(index) : "null";
        }

        /**
         * Linear Search O(n)
         *
         * @param cardNumber, Searching an index which is between start-end .
         * @return an index. If not found, return -1.
         */
        private Integer findIndexLinear(Long cardNumber) {

            for (int i = 1; i < startEndList.size(); i += 2) {
                if (startEndList.get(i - 1) <= cardNumber && cardNumber <= startEndList.get(i)) {
                    return i / 2;
                }
            }
            return -1;
        }

        /**
         * Binary Search O(logn)
         *
         * @param cardNumber, Searching an index which is between start-end .
         * @return an index. If not found, return -1.
         */
        private Integer findIndexBinary(Long cardNumber) {

            int left = 0;
            int right = startEndList.size();

            while (left < right) {

                int mid = left + (right - left) / 2;

                if (mid % 2 == 0 && startEndList.get(mid) <= cardNumber && cardNumber <= startEndList.get(mid + 1)) { // right pair (start-end) of mid
                    return mid / 2;
                } else if (mid % 2 == 1 && startEndList.get(mid - 1) <= cardNumber && cardNumber <= startEndList.get(mid)) {  // left pair
                    return mid / 2;
                }

                if (cardNumber < startEndList.get(mid)) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            }
            return -1;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
        
        File text = new File("D:/dev/CardSystem/src/cardsystem/input.txt");

        try (final Scanner scanner = new Scanner(text)) {
            List<Solution.BinRange> binRanges = new ArrayList<>();
            
            /*
            4111111111111111
            400000000000,499999999999,visa
            500000000000,599999999999,mc
            */

            String cardNumber = scanner.next();
            scanner.nextLine();

            scanner.useDelimiter("[,\n]");

            while (scanner.hasNext()) {
                String start = scanner.next();
                String end = scanner.next();
                String cardType = scanner.next();
                binRanges.add(new Solution.BinRange(start, end, cardType));
                if (scanner.hasNextLine()) {
                    scanner.nextLine();
                }
            }

            Solution.CardTypeCache cache = Solution.buildCache(binRanges);
            if (cache != null) {
                bufferedWriter.write(String.valueOf(cache.get(cardNumber)));
            }
        }

        bufferedWriter.newLine();
        bufferedWriter.close();
    }
}

What am I doing wrong, been scratching my head for hours

1
What you are doing incorrect is that you are using the wrong type to represent the credit card numbers. The number 4111,1111,1111,1111 is roughly 4.1 x 10^16. Integer.MAX_VALUE is 2^31 - 1 which is 2,147,483,647. A credit card number doesn't fit in an int.Stephen C
Hi @StephenC what is the best way?arriff
Use String to represent credit card numbers. They are not really integers at all. They are identifiers composed of a fixed number of decimal digits.Stephen C
And if this was a real application that handled real credit card numbers, it is recommended that you use a byte[] or char[] in-memory representation that can be erased after use. See Should credit card numbers be stored as strings or ints?Stephen C
Hi @StephenC can you help me out on the code with a solution if you don't mind?arriff

1 Answers

0
votes

When you print the output:

bufferedWriter.write(String.valueOf(cache.get(cardNumber)));

The cardNumber is "4111111111111111", and you look for it to be in one of the ranges.

4111 1111 1111 1111
4000 0000 0000

I think your card number is outside every range specified in your file. So you can edit your file, i.e.

FROM

400000000000,499999999999,visa
500000000000,599999999999,mc
400000000000,499999999999,visa
420008000000,420008999999,visadebit
420008000000,435000999999,visa
540008000000,599999999999,mc

TO

4000000000000000,4999999999999999,visa
5000000000000000,5999999999999999,mc
4000000000000000,4999999999999999,visa
4200080000000000,4200089999999999,visadebit
4200080000000000,4350009999999999,visa
5400080000000000,5999999999999999,mc