-1

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

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

arriff
  • 399
  • 1
  • 10
  • 30
  • 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 Feb 04 '22 at 15:00
  • Hi @StephenC what is the best way? – arriff Feb 04 '22 at 15:06
  • 2
    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 Feb 04 '22 at 15:12
  • 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?](https://stackoverflow.com/questions/7199559) – Stephen C Feb 04 '22 at 15:16
  • Hi @StephenC can you help me out on the code with a solution if you don't mind? – arriff Feb 04 '22 at 15:25
  • Yes, I do mind. You should be able to work it out a solution for yourself. Hint: take a look at the operations that are provided by `TreeMap`. If you use the `ceiling` and `floor` methods for looking up the credit card numbers. you can use the map to neatly represent ranges / bins. (Doing your homework yourself is a better way to learn to program.) – Stephen C Feb 04 '22 at 15:36
  • Hi @StephenC I choose to trim the number to 12 digits so as to much the bin, – arriff Feb 04 '22 at 16:35
  • There are a few ways to implement this. But the most important thing is that you learn how to work it out for yourself. – Stephen C Feb 05 '22 at 00:53
  • HI @StephenCapart from triming, what other ways do you have in mind? It is good i get more than one perspective on how to tackle this. – arriff Feb 05 '22 at 02:21
  • 1
    See my previous hint!!! If you use TreeMap to do the range search (in O(logN)) you can rip out your custom binary chop code. And even a simple loop or (yuck) a sequence of `if ... else if` tests (both O(N)) may be reasonable solutions if the number of bins (N) is small. – Stephen C Feb 05 '22 at 02:41
  • @StephenC thank you so much for taking time to answer, and what if the bin is huge, over 10million bins? what would be the best approach? – arriff Feb 05 '22 at 02:47
  • Possibly a database. Possibly a TreeMap. Possibly others. But that is a different problem to this one. – Stephen C Feb 05 '22 at 02:52

2 Answers2

1

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
Francesco Pitzalis
  • 2,042
  • 1
  • 16
  • 20
1

FROM

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

TO

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

you can try and trim the numbers to the left and omit everything that exceeds 12 digits and then you will get a print of the card type to the console

here is a line to add

 cardNumber = cardNumber.length() > 13 ? cardNumber.substring(0, 12) : cardNumber;

so the whole function will look like

public String get(String cardNumber) {

        cardNumber = cardNumber.length() > 13 ? cardNumber.substring(0, 12) : cardNumber;
        Integer index = findIndexLinear(Long.valueOf(cardNumber));    // Linear Search
        //Integer index = findIndexBinary(Long.valueOf(cardNumber));      // Binary Search
        return (index != -1) ? cache.get(index) : "null";
    }
arriff
  • 399
  • 1
  • 10
  • 30
Remy
  • 788
  • 1
  • 9
  • 14