# Thu, 20 Aug 2026 13:11:52 GMT
---
version: 23
data:


- type: ModificationMethod
  attributes:
    displayName: Credit card number
    description: |
          Generates a credit card number. The first parameter should be an existing credit card number. The second parameter specifies how many digits are copied unchanged from the original number.
    scriptLanguage: "GROOVY"
    legacyScript: false
    inputType: "COLUMN"
    mappingTableContainers: []
    mappingTableAccessMode: READ
    code: |
        /*
         * *****************************************************************************
         * Credit Card Number
         * *****************************************************************************
         *
         * Generates a bank name based on the ﬁrst parameter. The second parameter speciﬁes
         * the modificationMethod.country for the bank name.
         *
         *
         * The following variables are available in this script:
         * =====================================================
         * ctx             : A context object that offers a number of methods.
         *                   The method indexOf() can be used to get the index of a
         *                   specific column within the data.
         * data       : The data array. This array contains the contents of all
         *                   columns of the current row of the table that is being
         *                   masked by this method.
         *
         * Additionally for column based modification methods the following two
         * variables are available:
         * columnName      : The name of the column that matched the pattern.
         * columnIndex     : The position of the column that matched the pattern within
         *                   the data array. This value is zero-based.
         * *****************************************************************************
         */
        import de.ubs.xdm3.batch.modification.ModificationUtils
        import de.ubs.xdm3.batch.modification.ModificationException
        import de.ubs.xdm3.batch.modification.CompositeKey
        import de.ubs.xdm3.batch.modification.HashUtils
        import java.math.BigDecimal
        import java.math.RoundingMode
        
        /**
         * This function is called once before the processing starts. The method
         * is called once per table.
         */
        def init() {
        }
        
        /**
         * This function should modify the row that is provided by the data. The
         * method is called for each single row of the table.
         */
        def apply() {
            def value =  modificationMethod.seed
        
            value = value.toString()
            if (!(value.isNumber()) || value.toBigDecimal() < 0) {
                value = HashUtils.hashInt(HashUtils.hashStringLikeInt(value))
                value = value.toBigDecimal()
                def shift = Math.pow(10, 16 - (Math.floor(Math.log10(value)) + 1))
                value = (value * shift + (HashUtils.hashInt(HashUtils.hashStringLikeInt(value.toString())) % shift))
            } else {
                value = value.toBigDecimal()
            }
        
            value = Math.floor(value)
            if ((Math.floor(Math.log10(Math.floor(value))) + 1) != 16) {
                if (value == 0) {
                    value = 1.toBigDecimal()
                }
                def shift = Math.pow(10, 16 - (Math.floor(Math.log10(value)) + 1))
                value = (value * shift + (HashUtils.hashInt(HashUtils.hashStringLikeInt(value.toString())) % shift))
            }
            def Hex100000000 = new BigDecimal("2147483648")
            def l = (Math.floor(Math.log10(value.toBigDecimal().doubleValue())) + 1) as int
        
            def retain = modificationMethod.retain as int
        
            if (retain >= l) {
                data[columnIndex] = value
                return true
            }
        
            def a = value.toBigDecimal()
                    .divide(new BigDecimal("4294967296"), 0, RoundingMode.DOWN)
                    .toBigInteger()
                    .and(new BigInteger("2147483647"))
        
            def b = value.toBigDecimal().toBigInteger().and(new BigInteger("2147483647"))
        
            def m = new BigDecimal(HashUtils.hashInt((a ^ b) as int))
            def n = new BigDecimal(HashUtils.hashInt(m as int))
            def s = m.multiply(Hex100000000).add(n)
            s = s.remainder(BigDecimal.TEN.pow(l))
        
            if (retain > 0) {
                def v = value.toBigDecimal()
                def left = v.subtract(v.remainder(BigDecimal.TEN.pow(l - retain)))
                def right = s.remainder(BigDecimal.TEN.pow(l - retain))
                s = left.add(right)
            }
            def r = HashUtils.luhn(s as long)
            data[columnIndex] = r.toString()
            return true
        }
        /*
         * This function is executed once after the modification of a table is complete. The method
         * is called once per table.
         */
        def close() {
        }
    parameters:
    - displayName: Seed
      description: |
          The original credit card number.
      variableName: seed
      dataType: STRING
    - displayName: Retain
      description: |
          The number of digits to retain from the original number.
      variableName: retain
      dataType: NUMBER
